教训:nacos配置一定要验证可用性之后再去验证性能
feign+okhttp 与 feign+httpclient开启压缩的方式是不一样的!
服务端返回值开启压缩
服务端添加配置
1 2 3 4 5 6 7 8 9 10
| server: compression: enabled: true min-response-size: 100 mime-type: - text/html - text/xml - application/xml - application/octet-stream - application/json
|
客户端接收服务端返回信息进行自动解压缩
okhttp
自带自动解压缩,代码在okhttp3.internal.http.BridgeInterceptor
httpclient
未知
测试验证
1 2 3 4 5 6 7 8
| http POST http://127.0.0.1:8080/ping HTTP/1.1 200 Connection: close Content-Encoding: gzip Content-Type: application/json Date: Fri, 03 Feb 2023 04:56:39 GMT Transfer-Encoding: chunked Vary: accept-encoding
|
客户端发送压缩请求
方案1: feign_okhttp_undertow
此方案可以利用undertow的自动解压缩能力,方便快捷,但是风险点是会影响现有配置及监控(我们有大量的tomcat配置及监控)。
服务端添加如下依赖配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| server: undertow: io-threads: 64 worker-threads: 500 compression: enabled: true min-response-size: 100 mime-type: - text/html - text/xml - application/xml - application/octet-stream - application/json
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration public class WebServerConfig {
@Bean public UndertowServletWebServerFactory undertowServletWebServerFactory() { UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(); factory.addDeploymentInfoCustomizers((deploymentInfo -> deploymentInfo.addInitialHandlerChainWrapper(httpHandler -> new RequestEncodingHandler(httpHandler).addEncoding("gzip", GzipStreamSourceConduit.WRAPPER) ) )); return factory; } }
|
客户端添加如下配置
1 2 3 4 5 6
| feign: client: okhttp: enabled: true httpclient: enabled: false
|
1 2 3 4 5
| <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> <version>${version.feign.okhttp}</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12
| @Configuration @Slf4j public class OkHttpConfig { @Bean public OkHttpClient okHttpClient() { return new OkHttpClient.Builder() .addInterceptor(new OkGzipCompressionInterceptor()) .connectTimeout(2, TimeUnit.SECONDS) .connectionPool(new ConnectionPool(500, 5L, TimeUnit.MINUTES)) .build(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Slf4j public class OkGzipCompressionInterceptor implements Interceptor { @NotNull @Override public Response intercept(@NotNull Chain chain) throws IOException { Request originalRequest = chain.request(); if (originalRequest.body() == null || !StringUtils.equalsIgnoreCase(originalRequest.header("Content-Encoding"), "gzip")) { return chain.proceed(originalRequest); }
Request compressedRequest = originalRequest.newBuilder() .header("Content-Encoding", "gzip") .method(originalRequest.method(), compress(originalRequest.body())) .build(); return chain.proceed(compressedRequest); }
private RequestBody compress(RequestBody body) { return new RequestBody() { @Nullable @Override public MediaType contentType() { return body.contentType(); }
@Override public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException { BufferedSink gzipSink = Okio.buffer(new GzipSink(bufferedSink)); body.writeTo(gzipSink); gzipSink.getBuffer().size(); gzipSink.close(); } }; } }
|
方案2: feign_okhttp_tomcat
服务端配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class GZIPUtils {
public static byte[] uncompress(byte[] src) throws IOException { if (src == null || src.length == 0) { return null; }
ByteArrayInputStream in = new ByteArrayInputStream(src); return uncompress(in); }
public static byte[] uncompress(InputStream srcStream) throws IOException { if (srcStream == null || srcStream.available() == 0) { return null; }
ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPInputStream ungzip = new GZIPInputStream(srcStream); byte[] buffer = new byte[256]; int n; while ((n = ungzip.read(buffer)) >= 0) { out.write(buffer, 0, n); } return out.toByteArray(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
@Slf4j public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
private Map<String, String[]> parameterMap;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request, boolean isCompress) throws IOException { super(request); if (isCompress) { InputStream in = request.getInputStream(); if (in != null) { body = readGzipBytes(in); } else { body = new byte[0]; } } else { BufferedReader reader = request.getReader(); if (reader != null) { body = readBytes(reader); } else { body = new byte[0]; } } parameterMap = request.getParameterMap(); }
private byte[] readGzipBytes(InputStream srcStream) throws IOException { return GZIPUtils.uncompress(srcStream); }
@Override public BufferedReader getReader() throws IOException {
ServletInputStream inputStream = getInputStream();
if (null == inputStream) { return null; }
return new BufferedReader(new InputStreamReader(inputStream)); }
@Override public Enumeration<String> getParameterNames() { Vector<String> vector = new Vector<>(parameterMap.keySet()); return vector.elements(); }
@Override public ServletInputStream getInputStream() throws IOException {
if (body == null) { return null; }
final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() {
@Override public boolean isFinished() { return false; }
@Override public boolean isReady() { return false; }
@Override public void setReadListener(ReadListener listener) {
}
@Override public int read() throws IOException { return bais.read(); } }; }
private byte[] readBytes(BufferedReader br) throws IOException { String str; StringBuilder retStr = new StringBuilder(); while ((str = br.readLine()) != null) { retStr.append(str); } if (StringUtils.isNotBlank(retStr.toString())) { return retStr.toString().getBytes(StandardCharsets.UTF_8); } return new byte[0]; }
public String inputStream2String(InputStream inputStream) { if (Objects.isNull(inputStream)) { return ""; }
StringBuilder sb = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { log.error("Fail to populate request body", e); } return sb.toString(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| @Component @WebFilter(urlPatterns = "/*") @Slf4j public class RequestContentFilterHaha implements Filter {
private final static String CONTENT_ENCODING = "Content-Encoding"; private final static String GZIP_TYPE = "gzip";
@Override public void init(FilterConfig filterConfig) throws ServletException { log.info("Initializing request content filter ..."); }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { if (servletRequest instanceof HttpServletRequest) { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; try { String encodeType = httpServletRequest.getHeader(CONTENT_ENCODING); BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest, GZIP_TYPE.equals(encodeType)); filterChain.doFilter(requestWrapper, servletResponse); } catch (Exception e) { if (log.isDebugEnabled()) { log.warn("read request request body error: {}, url: {}, method: {}", e.getMessage(), httpServletRequest.getRequestURL(), httpServletRequest.getMethod()); } } } else { filterChain.doFilter(servletRequest, servletResponse); } }
@Override public void destroy() {
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| server: compression: enabled: true min-response-size: 200 mimeTypes: - text/html - text/xml - application/xml - application/octet-stream - application/json feign: okhttp: enabled: true httpclient: enabled: false max-connections: 1000 max-connections-per-route: 50 ribbon: http: client: enabled: false okhttp: enabled: true
|
客户端配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| feign: okhttp: enabled: true httpclient: enabled: false max-connections: 1000 max-connections-per-route: 50 ribbon: http: client: enabled: false okhttp: enabled: true
|
1 2 3 4 5
| <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> <version>${version.feign.okhttp}</version> </dependency>
|