当前位置: 首页 > 科技观察

OkHttp透明压缩,10倍收割性能,加个毛病

时间:2023-03-12 21:38:38 科技观察

转载本文请联系味小姐公众号。要使用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服务的链接。这种压缩方式对于数据量大的业务来说简直是救命啊!具体配置如下。服务器:端口:8082压缩:已启用:truemin-response-size:1024mime-types:[“text/html”,“text/xml”,“application/xml”,“application/json”,“application/octet-stream”]其对应的Spring配置类为org.springframework.boot.web.server.Compression。但不要太兴奋。因为是分布式环境,调用链会比较长。即使在内网,十几MB的网络传输也需要相当长的时间。如上图所示,一个请求从浏览器到真正的服务节点可能会经过很多链接。nginx将请求转发给微服务网关zuulzuul转发给具体的微服务A微服务A通过Feign接口调用微服务B如果我们的大部分数据都是由微服务B提供的,以上任何一个环节传输效率慢,都会影响请求的性能。因此,我们需要在Feign界面开启gzip压缩。使用OkHttp的透明代理是最简单的方法。首先在项目中引入feign的jar包。dependency>io.github.openfeignfeign-okhttp其次,在yml文件中启用OkHttp作为feign的客户端请求工具包。为了保险起见,我们同时屏蔽了httpclient,这个东西太重太老了。feign:httpclient:enabled:falseokhttp:enabled:true至此,我们就可以享受到OkHttp透明代理带来的便利了。如果你的应用数据包很大,调用链很长,这个方法甚至可以提升你服务几秒钟的性能。xjjdog曾经通过调整几个参数让蜗牛系统飞起来。大家惊呼:原来B端也可以是C端,OkHttp是如何实现透明压缩的呢?OkHttp通过拦截器进行透明压缩处理。具体类是okhttp3.internal.http.BridgeInterceptor。具体代码如下。当判断没有Accept-Encodingheader时,自己加一个。//Ifweaddan"Accept-Encoding:gzip"headerfield我们也负责解压缩//thetransferstream.booleantransparentGzip=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)){GzipSourceresponseBody=newGzipSource(networkResponse.body().source());HeadersstrippedHeaders=networkResponse(.newBuilder().removeAll("内容编码").removeAll("内容长度").build();responseBuilder.headers(strippedHeaders);StringcontentType=networkResponse.header("Content-Type");responseBuilder.body(newRealResponseBody(contentType,-1L,Okio.buffer(responseBody)));}可以看到if语句中有3个条件。程序没有设置Accept-Encoding,启用了透明压缩。服务器有一个Content-Encoding标头,并且启用了gzip压缩。只有同时有满足这三个条件的数据包,OkHttp的透明压缩才会起作用,帮我们自动解压。它挖的坑有点深。不幸的是,上面的关键代码是onlyif,notelse,即当其中任何一个条件不满足时,后端数据包都会原封不动的返回。2、3这两个条件没有问题,后端数据原样返回也没什么坏处,问题出在第一个条件上。如果您在代码中,请使用以下代码:Request.Builderbuilder=chain.request().newBuilder().addHeader("Accept","application/json").addHeader("Accept-Encoding","gzip");即手动设置Accept-Encoding头信息。这是很常见的,因为它体现了程序员思维的严谨性。正是这种严格造成了问题。如果你的后端应用一开始没有开启gzip压缩,这时候两者就没问题了;但是如果有一天你的后端应用程序突然启用了gzip压缩,你的代码就完蛋了。原因是服务端会原样返回gzip数据包,需要手动处理gzip数据包。所以,不加是好事,加了就是坏事,除非你想自己处理gzip数据。因为OkHttp在Android上也被广泛使用,如果不知道这个细节,后果不堪设想。客户端更新慢,只能老老实实回滚服务器。智能的背后,总有一些肉眼看不到的细节。就像xjjdog的天真,背后总藏着羞涩。只有深入了解,才会知道它的美。作者简介:品味小姐姐(xjjdog),一个不允许程序员走弯路的公众号。专注于基础架构和Linux。十年架构,每天百亿流量,与你探讨高并发世界,给你不一样的滋味。我的个人微信xjjdog0,欢迎加好友进一步交流。