Spring boot 使用 feign 调用参数过长(Post
服务使用之间如果使用 feign
相互调用的话,无论是 POST
或 GET
请求,如果携带的数据过长的话,会导致丢失部分数据或者报错。解决方法很简单。就是加大服务提供者的限制,如下:
修改 yml
或 properties
配置文件:
server:
port: 4450
# 增加请求头接受大小
max-http-header-size: 10485760
1. 问题排查过程
1.1 现象
调用说明:A服务调用B服务,前端提交到A服务的时候数据比较少,经过A加工后(数据多),调用B进行处理。
- 服务A调用服务B的时候,服务A 抛出异常,状态码是
400
,而B服务没有异常。 -
控制台信息
w400
1.2 问题排查
-
一开始以为是发送方做了数据控制,截断了数据的发送。于是乎觉得是
fegin
的HttpClient
的问题?使用Charles
调试数据请求,发现不是。这样就断定了是服务提供者的问题。 -
后面想起
spring boot
也是使用tomcat
容器部署的,tomcat
本身也存在数据请求的限制,找到了POST
相关配置如下:
# tomcat 配置
server.tomcat.max-http-form-post-size
# jetty 配置
server.jetty.max-http-form-post-size
还是解决不了。
2. 疑惑
2.1 为什么是是配置 max-http-header-size 就可以了。难道是 fegin
是把请求放到头部?需要需要重新分析请求的数据?
官方对此参数的说明
Key Value | Default | Description |
---|---|---|
server.max-http-header-size | 8KB | Maximum size of the HTTP message header. |
依据说明寻找 HTTP message header
规范
根据不同上下文,可将消息头分为:
- General headers: 同时适用于请求和响应消息,但与最终消息主体中传输的数据无关的消息头。
- Request headers: 包含更多有关要获取的资源或客户端本身信息的消息头。
- Response headers: 包含有关响应的补充信息,如其位置或服务器本身(名称和版本等)的消息头。
- Entity headers: 包含有关实体主体的更多信息,比如主体长(Content-Length)度或其MIME类型。
摘抄于 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers
并未找到相关头部具体的定义消息,但经过实际的代码试验,URL
就是头部的一部分,默认是 8KB
。超过后就会拒绝。(在头部设置超过 8KB
的数据也是同样的异常)。
控制台报错的错误
2.2 找出 fegin 拼接到url上的原因
通过 debug 找到关键方法:
//解析好了fegin 配置,准备调用远程方法
SynchronousMethodHandler#invoke(Object[] argv);
//执行远程调用和编码,
SynchronousMethodHandler#executeAndDecode(RequestTemplate template);
//这里转换成 Request 对象的时候,已经把参数拼接到 URL 上了,也就是说 POST 变成了GET 请求
SynchronousMethodHandler#targetRequest(RequestTemplate template)
也就是说,如果要彻底解决问题,需要更换底层相关实现。另外如果可以修改源代码,解决也很简单,只需要依据请求的方法来构建是否把数据放到 body 还是 url 上面即可。
2.3 使用 httpClient 代替默认实现
增加 maven
依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-httpclient -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.8</version>
</dependency>
在yml文件中增加配置:
feign:
httpclient:
enabled: true
增加后启动依旧POST 变GET 请求。
以下是原因分析:
先回经过 ApacheHttpClient#toHttpUriRequest(Request request, Request.Options options);
方法,由于前面构建的 Request
对象就没有 body
信息。所以默认赋予一个空的Entity。如下图:
其次调用了 RequestBuilder#build();
构建的时候,下图②处又将重新参数赋予到url上了,又重复出现一样的问题。如下图:
再寻解决方案。。。
思考
问:同样的数据,如果直接在使用前端的 Ajax 请求的话,会缺失数据吗?
如果正常使用POST请求,不会那么快就受到限制,目前看到的是 spring 的fegin用了get请求,把参数写到url上,导致长度受限 8K
参考文献
https://www.jianshu.com/p/11710629c226
HTTP Body 相关规范
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Messages
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/POST