OkHttp透明压缩,收获性能10倍,外加故障一枚

标签: okhttp 透明 压缩 | 发表时间:2020-09-16 12:06 | 作者:小姐姐味道
出处:https://juejin.im/backend

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。

要使用 OkHttp,一定要知道它的 透明压缩,否则死都不知道怎么死的;或者活也不知道为什么活的不舒坦。

反正不是好事。

什么叫 透明压缩呢?OkHttp在发送请求的时候,会自动加入gzip请求头 Accept-Encoding:gzip。所以,当返回的数据带有gzip响应头时 Content-Encoding=gzip,OkHttp会自动帮我们解压数据。( Accept-EncodingContent-Encoding是一对请求头,分别对应着请求和返回)

为什么要进行压缩呢?因为它能大幅减少传输的容量。像一些CPU资源占用不高的服务,比如Kafka,我们就可以开启gzip压缩,加快信息的流转。

这个压缩比有多高呢?可以看下下面实实在在的截图,对于普通的 xml或者 json,数据可以由 9MB压缩到 350KB左右,压缩比足足达到了 26

它让系统性能飞起来

SpringCloud微服务体系,现在有非常多的公司在用。即使是一些传统企业,一些大数据量的 toB企业,也想尝一尝螃蟹。

对于一个简单的SpringBoot服务,我们只需要在yml文件中配置上相应的压缩就可以了。这样,我们就打通了浏览器到Web服务的这一环。这种压缩方式,对于大数据量的服务来说,是救命式的!

具体配置如下。

   server:
  port: 8082
  compression:
    enabled: true
    min-response-size: 1024
    mime-types: ["text/html","text/xml","application/xml","application/json","application/octet-stream"]
复制代码

它所对应的Spring配置类是 org.springframework.boot.web.server.Compression

但是不要高兴太早。由于是分布式环境,这里面调用链就会长一些。即使是在内网,动辄十几MB的网络传输,也会耗费可观的时间。

如上图,一个请求从浏览器到达真正的服务节点,可能要经过很多环节。

  • nginx转发请求到微服务网关zuul
  • zuul转发到具体的微服务A
  • 微服务A通过Feign接口调用微服务B

如果我们的数据,大多数是由微服务B提供的,那么上面的任何一个环节传输效率慢,都会影响请求的性能。

所以,我们需要开启Feign接口的gzip压缩。使用OkHttp的透明代理是最简单的方式。

首先,在项目中引入feign的jar包。

   dependency>
            <groupId>io.github.openfeigngroupId>
            <artifactId>feign-okhttpartifactId>
dependency>
复制代码

其次,在yml文件中启用OkHttp作为feign的客户端请求工具包。稳妥起见,我们同时屏蔽了httpclient,这个东西太重太老了。

   feign:
  httpclient:
    enabled: false
  okhttp:
    enabled: true
复制代码

到此为止,我们就可以享受OkHttp的透明代理带来的便捷性了。

假如你的应用数据包大,调用链长,这种方式甚至会给你的服务带来 数秒的性能力提升。xjjdog就曾经靠调整几个参数,就让一个蜗牛系统飞了起来。大家惊呼:原来B端也可以C一下。

OkHttp是如何实现透明压缩的?

OkHttp对于透明压缩的处理,是通过拦截器来做的。具体的类,就是 okhttp3.internal.http.BridgeInterceptor

具体代码如下,当判断没有 Accept-Encoding头的时候,就自行加入一个。

   // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
  transparentGzip = true;
  requestBuilder.header("Accept-Encoding", "gzip");
}
复制代码

最关键的代码在下面。

   if (transparentGzip
    && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
    && HttpHeaders.hasBody(networkResponse)) {
  GzipSource responseBody = new GzipSource(networkResponse.body().source());
  Headers strippedHeaders = networkResponse.headers().newBuilder()
      .removeAll("Content-Encoding")
      .removeAll("Content-Length")
      .build();
  responseBuilder.headers(strippedHeaders);
  String contentType = networkResponse.header("Content-Type");
  responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
复制代码

可以看到 if语句里,有三个条件。

  • 程序没有设置 Accept-Encoding,启用了透明压缩
  • 服务端有 Content-Encoding头,并启用了gzip压缩
  • 有数据包

只有同时满足这三个条件,OkHttp的透明压缩才会起作用,帮我们自动解压。

它挖的坑有点深

可惜的是,上面的关键代码,只有 if,没有 else,也就是当其中的任何一个条件不满足,后端的数据包将原封不动的返回。

2、3两个条件是没有什么问题的,原样返回后端数据并没有什么损害,问题就出在第一个条件里。

如果你在代码中,使用了下面的代码:

   Request.Builder builder = chain.request()
                .newBuilder()
                .addHeader("Accept", "application/json")
                .addHeader("Accept-Encoding", "gzip");
复制代码

也就是手动设置了 Accept-Encoding头信息。这很常见,因为这体现了程序员思维的严谨。

正是这种严谨,造成了问题。

假如你的后端应用刚开始是没有开启 gzip压缩的,这时候两者相安无事;但如果你的后端应用突然有一天开启了 gzip压缩,你的这段代码将全部over。

原因就是,服务端gzip数据包会原样返回,你需要手动处理gzip数据包。

所以,不加是好事,加了反而会坏事,除非你想自己处理gzip数据。

由于 OkHttpAndroid上应用也非常广泛,如果你不知道这个细节,造成的后果就是灾难性的。客户端更新慢,只能老老实实回退服务端了。

智能的背后,总有些肉眼不可见的细节。就像是xjjdog纯情的背后,总有一份羞涩。只有深入了解,你才会知道它的美。

作者简介: 小姐姐味道 (xjjdog),一个不允许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不一样的味道。

相关 [okhttp 透明 压缩] 推荐:

OkHttp透明压缩,收获性能10倍,外加故障一枚

- - 掘金后端
原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处. 要使用 OkHttp,一定要知道它的 透明压缩,否则死都不知道怎么死的;或者活也不知道为什么活的不舒坦. OkHttp在发送请求的时候,会自动加入gzip请求头 Accept-Encoding:gzip. 所以,当返回的数据带有gzip响应头时 Content-Encoding=gzip,OkHttp会自动帮我们解压数据.

OkHttp,一次无奈的使用

- - 掘金后端
一次使用OKHTTP的心痛历程. 最近由于一些不得已的原因,接触到了OKHttp,说起来也挺Dan疼的,之前同事将生产附件上传地址配置成了测试地址,还好数量不多,没有造成太大的影响,况且的是这位同事又离职了,当时只能在心中 默默的问候了他N遍,当然问候完了之后,也不得不继续数据同步的工作. OKHTTP官方地址: okHttp.

Rxjava+ReTrofit+okHttp深入浅出-终极封装

- - CSDN博客推荐文章
Rxjava+ReTrofit+okHttp深入浅出-终极封装. 学习Rxjava和retrofit已经很长时间了,功能确实很强大,但是使用起来还是有点复杂,代码的重复性太高,所以决定把基于retrofit和rxjava的处理统一封装起来,实现的功能:. 1.Retrofit+Rxjava+okhttp基本使用方法.

Android Https相关完全解析 当OkHttp遇到Https

- - CSDN博客推荐文章
本文出自: 【张鸿洋的博客】. 其实这篇文章理论上不限于okhttp去访问自签名的网站,不过接上篇博文了,就叫这个了. 首先要了解的事,okhttp默认情况下是支持https协议的网站的,比如 https://www.baidu.com, https://github.com/hongyangAndroid/okhttp-utils等,你可以直接通过okhttp请求试试.

String压缩 解压缩

- - CSDN博客推荐文章
数据传输时,有时需要将数据压缩和解压缩,本例使用GZIPOutputStream/GZIPInputStream实现. 1、使用ISO-8859-1作为中介编码,可以保证准确还原数据. 2、字符编码确定时,可以在decompress方法最后一句中显式指定编码. * @return 压缩后的字符串. GZIPOutputStream os = null; // 使用默认缓冲区大小创建新的输出流.

Nginx GZip 压缩

- - 开心平淡对待每一天。热爱生活
  Nginx GZip 模块文档详见: http://wiki.nginx.org/HttpGzipModule 常用配置片段如下:. # 压缩比例,比例越大,压缩时间越长. 默认是1 gzip_types. text/css text/javascript; # 哪些文件可以被压缩 gzip_disable.

HDFS-压缩

- - Java - 编程语言 - ITeye博客
文件压缩带来了两大益处1)减少存贮空间2)加速网络(磁盘)传输. 基于大数据的传输,都需要经过压缩处理. 压缩格式 工具 算法 文件扩展名 可分块. Java代码 复制代码 收藏代码. 24.        // io.compression.codecs 定义列表中的一个 . Native gzip 库减少解压缩时间在50%,压缩时间在10%(同java实现的压缩算法).

Doclist压缩方法简介

- flychen50 - 搜索技术博客-淘宝
本文是作者在学习doclist压缩时的一点总结,希望以尽可能简单明了的方式描述各个算法的思想和适用场景,帮助同学们理解和比较. 本文并不涉及具体的算法实现,代码请大家自行google. 这里需要强调的是“所谓的改进顺序”只是作者yy出来方便理解记忆,并不反应真实的压缩方法发展历程. 倒排表的基本组成部分,看例子:.

uglifyjs批量压缩js

- - JavaScript - Web前端 - ITeye博客
jquery官方使用uglifyjs进行压缩的,压缩比较高. uglifyjs的安装方法. . 前端js压缩,使用uglifyjs压缩当前目录里的所有js文件,. 压缩后,会将原文件替换为压缩过的文件.

android图片压缩方法

- - CSDN博客移动开发推荐文章
第一:我们先看下质量压缩方法.         image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中  .         while ( baos.toByteArray().length / 1024>100) {  //循环判断如果压缩后图片是否大于100kb,大于继续压缩         .