温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

OkHttp如何实现透明压缩

发布时间:2022-02-24 11:18:19 来源:亿速云 阅读:146 作者:iii 栏目:开发技术

这篇文章主要介绍了OkHttp如何实现透明压缩的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇OkHttp如何实现透明压缩文章都会有所收获,下面我们一起来看看吧。

什么叫透明压缩呢?OkHttps 在发送请求的时候,会自动加入 gzip 请求头Accept-Encoding:gzip。所以,当返回的数据带有 gzip 响应头时Content-Encoding=gzip,OkHttps 会自动帮我们解压数据。(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 压缩。使用 OkHttps 的透明代理是最简单的方式。

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

<dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
</dependency>

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

feign:
  httpclient:
    enabled: false
  okhttp:
    enabled: true

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

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

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

OkHttps 对于透明压缩的处理,是通过拦截器来做的。具体的类,就是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压缩

  • 有数据包

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

它挖的坑有点深

可惜的是,上面的关键代码,只有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上应用也非常广泛,如果你不知道这个细节,造成的后果就是灾难性的。客户端更新慢,只能老老实实回退服务端了。

关于“OkHttp如何实现透明压缩”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“OkHttp如何实现透明压缩”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI