在微服务开发中SpringCloud全家桶集成了OpenFeign用于服务调用,SpringCloud的OpenFeign使用SpringMVCContract来解析OpenFeign的接口定义。

但是SpringMVCContract的Post接口解析实现有个巨坑,就是如果使用的是@RequestParam传参的Post请求,参数是直接挂在URL上的。

问题发现与分析

最近线上服务器突然经常性出现CPU高负载的预警,经过排查发现日志出来了大量的OpenFeign跨服务调用出现400的错误(HTTP Status 400)。

一般有两种情况:

  1. nginx 返回400
  2. java应用返回400

通过分析发现400是java应用返回的,那么可以确定是OpenFeign客户端发起跨服务请求时出现异常了。

但是查看源码发现出现这个问题的服务接口非常简单,就是一个只有三个参数的POST请求接口,这个错误并不是必现的错误,而是当参数值比较长(String)的时候才会出现。

所以可以初步确认可能是参数太长导致请求400,对于POST请求因参数太长导致400错误非常疑惑,POST请求除非把参数挂在URL上,否则不应该出现400才对。

问题排查

为了验证上面的猜测,手写了一个简单的示例,在验证过程中特意开启了OpenFeign的debug日志。

首先编写服务接口

这是一个简单的Post接口,仅有一个参数(这里的代码仅用于验证,非正式代码)

  1. @FeignClient(name = "user-service-provider")
  2. public interface HelloService {
  3. @PostMapping("/hello")
  4. public String hello(@RequestParam("name") String name);
  5. }

编写服务

服务这里随便写一个Http接口即可(同上,代码仅用于验证)

  1. @SpringBootApplication
  2. @RestController
  3. public class Starter {
  4. @RequestMapping("/hello")
  5. public String hello(String name) {
  6. return "User服务返回值:Hello " + String.valueOf(name);
  7. }
  8. public static void main(String[] args) {
  9. SpringApplication.run(Starter.class, args);
  10. }
  11. }

服务注册并调用

将服务注册到注册中心上,通过调用hello服务

  1. @Autowired
  2. public HelloService helloService;
  3. @RequestMapping("/hello")
  4. public String hello(String name) {
  5. return helloService.hello(name);
  6. }

通过日志可以发现,SpringCloud集成OpenFeign的POST请求确实是直接将参数挂在URL上,如下图:

正是因为这个巨坑,导致了线上服务器经常性高CPU负载预警。

问题解决

问题知道了,那么就好解决了,用SpringCloud官方的说法是可以使用@RequestBody来解决这个问题,但是@RequestBody的使用是有限制的,也就是参数只能有一个,而且需要整个调用链路都做相应的调整,这个代价有点高,这里不采用这种方式,而是采用RequestInterceptor来处理。

  1. 编写RequestInterceptor

这里将request的queryLine取下来放在body中,需要注意的是,只有body没有值的时候才能这么做。

  1. public class PostRequestInterceptor implements RequestInterceptor {
  2. @Override
  3. public void apply(RequestTemplate template) {
  4. if ("post".equalsIgnoreCase(template.method()) && template.body() == null) {
  5. String query = template.queryLine();
  6. template.queries(new HashMap<>());
  7. if (StringUtils.hasText(query) && query.startsWith("?")) {
  8. template.body(query.substring(1));
  9. }
  10. template.header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
  11. }
  12. }
  13. }
  1. 配置RequestInterceptor
  1. feign:
  2. client:
  3. config:
  4. default:
  5. requestInterceptors: cn.com.ava.yaolin.springcloud.demo.PostRequestInterceptor
  1. 在下图可以看出请求参数不再挂在URL上了

@RequestBody的解决方案

问题虽然解决了,但采用的不是官方推荐的方案,这里将官方推荐的这种@RequestBody的解决方法也贴出来。

使用@RequestBody解决,需要4个步骤:

  1. 编写请求参数Bean
  1. public class HelloReqForm {
  2. private String name;
  3. public String getName() {
  4. return name;
  5. }
  6. public void setName(String name) {
  7. this.name = name;
  8. }
  9. }
  1. 调整接口声明
  1. @PostMapping("/hello")
  2. public String hello(@RequestBody HelloReqForm form);
  1. 调整服务调用
  1. HelloReqForm form = new HelloReqForm();
  2. form.setName(name);
  3. return helloService.hello(form);
  1. 调整服务实现
  1. @RequestMapping("/hello")
  2. public String hello(@RequestBody HelloReqForm form) {
  3. }
  1. 最终调用日志

涉及的Java类

最后列出一些涉及的java类:

  1. SpringMVCContract 服务接口解析
  2. RequestParamParameterProcessor 参数解析
  3. feign.RequestTemplate Rest请求构造器
  4. feign.Request 处理http请求

=========================================================



关注公众号,阅读更多文章。

SpringCloud OpenFeign Post请求的坑的更多相关文章

  1. 【BIGDATA】ElasticSearch HEAD插件的GET请求的坑

    今使用HEAD插件,发现复杂查询功能下,使用GET请求有坑. 查询语句如下: GET kk/_search { "query": { "match": { &q ...

  2. Spring-cloud (九) Hystrix请求合并的使用

    前言: 承接上一篇文章,两文本来可以一起写的,但是发现RestTemplate使用普通的调用返回包装类型会出现一些问题,也正是这个问题,两文没有合成一文,本文篇幅不会太长,会说一下使用和适应的场景. ...

  3. vue前端post请求之坑

    最近用的vue请求数据,坑死,还是对前端vue框架不熟. 与后端通信有问题,要么是json加入到url有问题.要么是json解析有问题. 解决方法: 1.请求参数一个用url传 var json=[{ ...

  4. 高并发场景-请求合并(一)SpringCloud中Hystrix请求合并

    背景 在互联网的高并发场景下,请求会非常多,但是数据库连接池比较少,或者说需要减少CPU压力,减少处理逻辑的,需要把单个查询,用某些手段,改为批量查询多个后返回. 如:支付宝中,查询"个人信 ...

  5. SpringCloud gateway自定义请求的 httpClient

    本文为博主原创,转载请注明出处: 引用 的 spring cloud gateway 的版本为 2.2.5 : SpringCloud gateway 在实现服务路由并请求的具体过程是在 org.sp ...

  6. datatables ajax后端请求那些坑

    在对datatables做后端数据填充的时候,遇到一个,翻页问题.在多次操作翻页后,总是提示加载中.反了很多博客没有找到原因. 经过测试,发现原来自己坑了自己. 代码如下: datatables : ...

  7. SpringCloud实战-Hystrix请求熔断与服务降级

    我们知道大量请求会阻塞在Tomcat服务器上,影响其它整个服务.在复杂的分布式架构的应用程序有很多的依赖,都会不可避免地在某些时候失败.高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险 ...

  8. Spring-cloud (八) Hystrix 请求缓存的使用

    前言: 最近忙着微服务项目的开发,脱更了半个月多,今天项目的初版已经完成,所以打算继续我们的微服务学习,由于Hystrix这一块东西好多,只好多拆分几篇文章写,对于一般对性能要求不是很高的项目中,可以 ...

  9. 注意:Tomcat Get请求的坑!

    Tomcat8.5,当Get请求中包含了未经编码的中文字符时,会报以下错误,请求未到应用程序在Tomcat层就被拦截了. Tomcat报错: java.lang.IllegalArgumentExce ...

随机推荐

  1. Java 10更新汇总,新的编译器通吃主流编程语言

    早些时候Oracle发布了Java 10,这是Oracle更改发布策略之后的第一版Java,Oracle现已决定每六个月发布一个新的Java版本,周期缩短了,但是并不是说我们要学的更多了,而是说缩短开 ...

  2. 019-链接 使用name属性

    路由设置: (r'/', index.IndexHandler), tornado.web.url(r'/lj', index.LJHandler, {"word3":" ...

  3. Wireshark中的Checksum: 0x90c5 [validation disabled]问题

    Wireshark中的Checksum: 0x90c5 [validation disabled]问题 废话不多说先上问题图: 这是我在做关于DNS协议PPT的时候出现的协议树第五项展开结果,可以发现 ...

  4. 大数据理论篇HDFS的基石——Google File System

    Google File System 但凡是要开始讲大数据的,都绕不开最初的Google三驾马车:Google File System(GFS), MapReduce,BigTable. 为这一切的基 ...

  5. Galera Cluster for MySQL 集群恢复

    node1: 1.rm -rf grastate.dat 2.mysqld_safe --wsrep-recover 3.galera_new_cluster node2: systemctl res ...

  6. Mac上Safari不能关键字搜索

    今天打开Mac,用Safari浏览器搜索的时发现不能进行关键字搜索,搜索栏只能打开网址. 现在问题已经解决,只要删除Safari上的cookies就可以了.操作步骤如下: Safari ->pr ...

  7. AtCoder Beginner Contest 177 题解

    AtCoder Beginner Contest 177 题解 目录 AtCoder Beginner Contest 177 题解 A - Don't be late B - Substring C ...

  8. 跟我一起学.NetCore之日志作用域及第三方日志框架扩展

    前言 上一节对日志的部分核心类型进行简单的剖析,相信现在再使用日志的时候,应该大概知道怎么一回事了,比如记录器是怎么来的,是如何将日志内容写入到不同目的地的等:当然还有很多细节没深入讲解,抽时间小伙伴 ...

  9. Android开发之制作圆形头像自定义View,直接引用工具类,加快开发速度。带有源代码学习

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 QQ986945193 博客园主页:http://www.cnblogs.com/mcxiaobing ...

  10. oeasy教您玩转linux010103我在那pwd

    我在哪pwd 回忆上次内容 我们上次列出了关于发行版详细信息文件的位置 ls /etc/*release 其中 ls是命令的名字,意味着list列表 /是整个文件系统的根root /etc是根目录下的 ...