OkHttp透明压缩,收获性能10倍,外加故障一枚
为什么要进行压缩呢?因为它能大幅减少传输的容量。像一些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.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</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数据。
由于OkHttp
在Android
上应用也非常广泛,如果你不知道这个细节,造成的后果就是灾难性的。客户端更新慢,只能老老实实回退服务端了。
智能的背后,总有些肉眼不可见的细节。就像是xjjdog纯情的背后,总有一份羞涩。只有深入了解,你才会知道它的美。
- (27) 剖析包装类 (中) / 计算机程序的思维逻辑
- Python量子力学计算模拟以及数据可视化
- (26) 剖析包装类 (上) / 计算机程序的思维逻辑
- (25) 异常 (下) / 计算机程序的思维逻辑
- (24) 异常 (上) / 计算机程序的思维逻辑
- Python3.6新特性官方文档中文版
- (23) 枚举的本质 / 计算机程序的思维逻辑
- (22) 代码的组织机制 / 计算机程序的思维逻辑
- Python开发微信公众号后台(系列二)
- (21) 内部类的本质 / 计算机程序的思维逻辑
- (20) 为什么要有抽象类? / 计算机程序的思维逻辑
- Python云计算框架:Openstack源码分析之RabbitMQ(一)
- (38) 剖析ArrayList / 计算机程序的思维逻辑
- 破解验证,让爬取更随心所欲!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- call,apply,bind 的完全实现和理 解
- js构造函数的理解
- 实战 | PyQt5制作雪球网股票数据爬虫工具
- 别再问我Python怎么操作Word了!
- Vaex :突破pandas,快速分析100GB大数据集
- 【适合收藏】为了多点时间陪女朋友,我向BAT大佬跪求了这15条JS技巧
- Flask框架教程汇总(1)---视图/重定向/传参
- OkHttp请求耗时统计
- Ubuntu 18.04 通过 Docker 快速部署 Smokeping 2.6.11 教程
- MySQL 8.0新特性 — 函数索引
- Docker快速上手指北(一)【技术创作101训练营】
- leetcode树之二叉搜索树的最近公共祖先
- 【技术创作101训练营】技术角 | 在CentOS 8上使用Nginx 1.18: 基本配置
- Java诊断应用之Arthas实战(技术创作101训练营)
- 突击并发编程JUC系列-数组类型AtomicLongArray