【OpenFeign】 Gzip 压缩

Metadata

title: 【OpenFeign】 Gzip 压缩
date: 2023-01-02 19:27
tags:
  - 行动阶段/完成
  - 主题场景/组件
  - 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
  - 细化主题/Module/OpenFeign/功能
categories:
  - OpenFeign
keywords:
  - OpenFeign
description: 【OpenFeign】 Gzip 压缩

Gzip 概述

gzip 是一种数据格式,采用 deflate 算法压缩数据,是一种流行的文件压缩算法,应用十分广泛,尤其是在 Linux 平台。

当 Gzip 压缩—个纯文本文件时,效果是非常明显的,大约可以减少 70% 以上的文件大小。

在网络数据经过压缩后实际上降低了网络传输的子节数,最明显的好处就是可以加快网页加载的速度。网页加载可以节省流量,改善用户的浏览体验。

HTTP 协议支持压缩说明

客户端向服务器发送请求时,如果消息头带有: Accept-Encoding:gzip, deflate字段,表示客户端支持的压缩格式 (gzip 或者 deflate),如果不发送该消息头,服务端默认是不会压缩的。

服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型压缩,就会对响应报文压缩之后返回给客户端,并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式进行压缩的。

客户端接收到请求之后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。

但是使用 GZIP 要增加服务器压缩的压力(cpu 消耗)、客户端解压缩的压力。

Tomcat 使用 Gzip 压缩

Spring Boot 在使用 Tomcat 时,可以直接通过配置开启 Gzip 压缩。

对应的配置类为Compression

  • enabled : 是否开启压缩,默认 false
  • mimeTypes : 压缩的媒体类型,默认是 JSON、页面等数据会进行压缩
  • excludedUserAgents :哪些浏览器访问时进行压缩
  • minResponseSize :响应数据最小多大,才压缩,默认 2K
public class Compression {
    private boolean enabled = false;
    private String[] mimeTypes = new String[]{"text/html", "text/xml", "text/plain", "text/css", "text/javascript", "application/javascript", "application/json", "application/xml"};
    private String[] excludedUserAgents = null;
    private DataSize minResponseSize = DataSize.ofKilobytes(2L);
}

可以在 YML 中开启压缩功能:

server:
  port: 9002
  compression:
    enabled: true

测试:编写一个返回大数据量的访问接口。

@GetMapping("/gzip")
    public Object gzip() {
        // 模拟给当前账户下单
        List<Account> list=new ArrayList<>();
        for (long i = 0L; i < 10000L; i++) {
            Account account=new Account();
            account.setAccountId(i);
            list.add(account);
        }
        return list;
    }

F12,可以看到请求、响应消息头,都添加了压缩的相关标识。

那么具体到底有没有压缩呢,由于压缩是客户端、WEB 服务器自己进行的,程序员无法感知。我们可以直接 Debug 到 Tomcat 的源码中,查看响应数据时,到底有没有进行压缩。

我们 Debug 到Http11Processor类的prepareResponse()方法,该类是 tomcat 中用于处理 http 请求的 Processor 的实现类,看下他的压缩处理逻辑:

boolean useCompression = false;
            // 判断是否进行压缩
            if (entityBody && this.sendfileData == null) {
                useCompression = this.protocol.useCompression(this.request, this.response);
            }

可以看到最后,useCompression变量为 true,说明确实进行了压缩处理。

Fegin 使用 Gzip 压缩

Fegin 是基于服务之前远程调用,所以不会像浏览器一样,请求自动添加压缩格式的支持。

Fegin压缩对应的配置类为FeignClientEncodingProperties,配置属性和 Tomcat 中的一个意思。

@ConfigurationProperties("feign.compression.request")
public class FeignClientEncodingProperties {
    private String[] mimeTypes = new String[]{"text/xml", "application/xml", "application/json"};
    private int minRequestSize = 2048;
}

然后看下压缩自动配置类FeignAcceptGzipEncodingAutoConfiguration,实际上就是注入了一个请求拦截器,在请求头中添加Accept-Encoding:gzip, deflate",但是在@ConditionalOnMissingBean中,如果使用的是 okhttp3 框架,则不会加载这个配置(那为啥用 okhttp 时,不会配置压缩呢?)。

@ConditionalOnProperty(
    value = {"feign.compression.response.enabled"},
    matchIfMissing = false
)
@ConditionalOnMissingBean(
    type = {"okhttp3.OkHttpClient"}
)
@AutoConfigureAfter({FeignAutoConfiguration.class})
public class FeignAcceptGzipEncodingAutoConfiguration {
    public FeignAcceptGzipEncodingAutoConfiguration() {
    }

    @Bean
    public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
        // 添加压缩拦截器 
        //  this.addHeader(template, "Accept-Encoding", new String[]{"gzip", "deflate"});
        return new FeignAcceptGzipEncodingInterceptor(properties);
    }
}

在源码中有说明: OK HTTP 客户端使用 “透明” 压缩,也是就默认就开启了压缩。。。啥都不用配。

为了验证这一点,首先将账户服务中的压缩配置都关闭掉,只在订单服务中添加响应压缩支持。

然后 Dubug 到订单服务,可以看到请求头中,自动添加了压缩消息头,最后也是使用压缩返回了数据,直接使用 Okhttp 时,不用再进行任何压缩配置了…。