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

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

[[342443]]

本文转载自微信公众号「小姐姐味道」,作者小姐姐养的狗 。转载本文请联系小姐姐味道公众号。  

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

反正不是好事。

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

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

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

 

它让系统性能飞起来

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

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

具体配置如下。

  1. server: 
  2.   port: 8082 
  3.   compression: 
  4.     enabled: true 
  5.     min-response-size: 1024 
  6.     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包。

  1. dependency> 
  2.             <groupId>io.github.openfeign</groupId> 
  3.             <artifactId>feign-okhttp</artifactId> 
  4. </dependency> 

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

  1. feign: 
  2.   httpclient: 
  3.     enabled: false 
  4.   okhttp: 
  5.     enabled: true 

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

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

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

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

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

  1. // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing 
  2. // the transfer stream. 
  3. boolean transparentGzip = false
  4. if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) { 
  5.   transparentGzip = true
  6.   requestBuilder.header("Accept-Encoding""gzip"); 

最关键的代码在下面。

  1. if (transparentGzip 
  2.     && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding")) 
  3.     && HttpHeaders.hasBody(networkResponse)) { 
  4.   GzipSource responseBody = new GzipSource(networkResponse.body().source()); 
  5.   Headers strippedHeaders = networkResponse.headers().newBuilder() 
  6.       .removeAll("Content-Encoding"
  7.       .removeAll("Content-Length"
  8.       .build(); 
  9.   responseBuilder.headers(strippedHeaders); 
  10.   String contentType = networkResponse.header("Content-Type"); 
  11.   responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody))); 

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

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

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

它挖的坑有点深

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

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

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

  1. Request.Builder builder = chain.request() 
  2.                 .newBuilder() 
  3.                 .addHeader("Accept""application/json"
  4.                 .addHeader("Accept-Encoding""gzip"); 

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

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

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

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

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

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

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

 

作者简介:小姐姐味道 (xjjdog),一个不允许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不一样的味道。我的个人微信xjjdog0,欢迎添加好友,进一步交流。

 

责任编辑:武晓燕 来源: 小姐姐味道
相关推荐

2015-01-04 15:36:52

XSS漏洞XSS

2019-04-11 14:16:31

数据产品经理数据科学数据

2024-09-02 14:37:50

2022-01-19 18:02:42

chrome插件使用者

2018-09-21 10:57:16

2018-01-19 15:38:16

戴尔

2015-04-16 09:38:23

2020-02-24 09:45:06

WindowsWindows 10微软

2018-08-16 17:20:49

小猪短租

2011-07-01 10:11:39

2018-05-10 11:06:56

华为云

2022-07-27 07:54:36

网络故障交换机

2020-10-30 07:09:52

Lombok架构师

2011-07-22 14:07:19

设计文化视觉设计设计

2014-03-26 10:00:06

RailsRails性能

2024-10-29 08:21:05

2016-03-03 17:39:32

未来范

2020-07-22 08:30:02

代码开发工具

2020-07-21 15:40:55

NginxJava服务器
点赞
收藏

51CTO技术栈公众号