记一次SpringBoot SpringCloud 版本升级Feign调用抛出400错误问题
问题背景
公司新的项目使用SpringBoot2.5.5 SpringCloud Hoxton.SR9版本搭建, Feign的相关配置沿用的老项目SpringBoot2.1.18.RELEASE SpringCloud Greenwich.RELEASE版本的配置, 结果运行时Feign远程调用抛出400参数过长错误, Feign调用的参数中包含大文本字段, 在老的项目中可以正常调用, 升级之后出现异常
Feign客户端使用的是POST+@RequestBody方式调用
Feign配置如下, 两个版本配置一样:
feign:client: config: default: connect-timeout: 5000 read-timeout: 120000compression: request: enabled: true response: enabled: trueokhttp: enabled: trueserver:max-http-header-size: 8192
排查过程
由于错误是在调用方抛出, 那基本上就是因为Feign在构造HttpConnection的过程中触发了这个错误, 我们可以发现升级之后feign-core的版本变更为10.10.1了, 我们只需要这个feign-core版本与之前的版本构造出来的请求有什么不同, 就能找出问题的原因了
找到feign客户端构造类feign.Feign.Builder#build
, 从这个入口处开始断点跟踪调用链
相关代码如下:
public Feign build() {......SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);......return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);}
根据上面的代码我们最终找到相关处理类feign.SynchronousMethodHandler
、feign.SynchronousMethodHandler#targetRequest
经过对此处代码的单步调试, 发现版本升级之后的fegin构造的request的headers多一个gzip相关的配置"Content-Encoding"
, feign.Client.Default#convertAndSend
相关代码:
HttpURLConnection convertAndSend(Request request, Options options) throws IOException {......// 这里获取出来的结果不一样// 2.5.5版本的可以获取到Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);boolean gzipEncodedRequest =contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);boolean deflateEncodedRequest =contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);
boolean hasAcceptHeader = false; Integer contentLength = null; for (String field : request.headers().keySet()) { if (field.equalsIgnoreCase("Accept")) { hasAcceptHeader = true; } for (String value : request.headers().get(field)) { if (field.equals(CONTENT_LENGTH)) { // 导致这里的contentLength不同 if (!gzipEncodedRequest && !deflateEncodedRequest) { contentLength = Integer.valueOf(value); connection.addRequestProperty(field, value); } } else { connection.addRequestProperty(field, value); } } } // Some servers choke on the default accept string. if (!hasAcceptHeader) { connection.addRequestProperty("Accept", "/"); } if (request.body() != null) { if (disableRequestBuffering) { // 最终的connection设置不同的contentLength if (contentLength != null) { connection.setFixedLengthStreamingMode(contentLength); } else { connection.setChunkedStreamingMode(8196); } } ...... } return connection; }
## 总结根据上述分析, 我们发现导致400的最终原因是由于不同版本的默认压缩配置不同, 升级之后的版本删除请求压缩配置(`compression.request.enabled`), 即可正常调用原文:https://juejin.cn/post/7102615499233558565