SpringCloud OpenFeign Post请求的坑
在微服务开发中SpringCloud全家桶集成了OpenFeign用于服务调用,SpringCloud的OpenFeign使用SpringMVCContract来解析OpenFeign的接口定义。
但是SpringMVCContract的Post接口解析实现有个巨坑,就是如果使用的是@RequestParam传参的Post请求,参数是直接挂在URL上的。
问题发现与分析
最近线上服务器突然经常性出现CPU高负载的预警,经过排查发现日志出来了大量的OpenFeign跨服务调用出现400的错误(HTTP Status 400)。
一般有两种情况:
- nginx 返回400
- java应用返回400
通过分析发现400是java应用返回的,那么可以确定是OpenFeign客户端发起跨服务请求时出现异常了。
但是查看源码发现出现这个问题的服务接口非常简单,就是一个只有三个参数的POST请求接口,这个错误并不是必现的错误,而是当参数值比较长(String)的时候才会出现。
所以可以初步确认可能是参数太长导致请求400,对于POST请求因参数太长导致400错误非常疑惑,POST请求除非把参数挂在URL上,否则不应该出现400才对。
问题排查
为了验证上面的猜测,手写了一个简单的示例,在验证过程中特意开启了OpenFeign的debug日志。
首先编写服务接口
这是一个简单的Post接口,仅有一个参数(这里的代码仅用于验证,非正式代码)
@FeignClient(name = "user-service-provider")
public interface HelloService {
@PostMapping("/hello")
public String hello(@RequestParam("name") String name);
}
编写服务
服务这里随便写一个Http接口即可(同上,代码仅用于验证)
@SpringBootApplication
@RestController
public class Starter {
@RequestMapping("/hello")
public String hello(String name) {
return "User服务返回值:Hello " + String.valueOf(name);
}
public static void main(String[] args) {
SpringApplication.run(Starter.class, args);
}
}
服务注册并调用
将服务注册到注册中心上,通过调用hello服务
@Autowired
public HelloService helloService;
@RequestMapping("/hello")
public String hello(String name) {
return helloService.hello(name);
}
通过日志可以发现,SpringCloud集成OpenFeign的POST请求确实是直接将参数挂在URL上,如下图:

正是因为这个巨坑,导致了线上服务器经常性高CPU负载预警。
问题解决
问题知道了,那么就好解决了,用SpringCloud官方的说法是可以使用@RequestBody来解决这个问题,但是@RequestBody的使用是有限制的,也就是参数只能有一个,而且需要整个调用链路都做相应的调整,这个代价有点高,这里不采用这种方式,而是采用RequestInterceptor来处理。
- 编写RequestInterceptor
这里将request的queryLine取下来放在body中,需要注意的是,只有body没有值的时候才能这么做。
public class PostRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
if ("post".equalsIgnoreCase(template.method()) && template.body() == null) {
String query = template.queryLine();
template.queries(new HashMap<>());
if (StringUtils.hasText(query) && query.startsWith("?")) {
template.body(query.substring(1));
}
template.header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
}
}
}
- 配置RequestInterceptor
feign:
client:
config:
default:
requestInterceptors: cn.com.ava.yaolin.springcloud.demo.PostRequestInterceptor
- 在下图可以看出请求参数不再挂在URL上了

@RequestBody的解决方案
问题虽然解决了,但采用的不是官方推荐的方案,这里将官方推荐的这种@RequestBody的解决方法也贴出来。
使用@RequestBody解决,需要4个步骤:
- 编写请求参数Bean
public class HelloReqForm {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 调整接口声明
@PostMapping("/hello")
public String hello(@RequestBody HelloReqForm form);
- 调整服务调用
HelloReqForm form = new HelloReqForm();
form.setName(name);
return helloService.hello(form);
- 调整服务实现
@RequestMapping("/hello")
public String hello(@RequestBody HelloReqForm form) {
}
- 最终调用日志

涉及的Java类
最后列出一些涉及的java类:
- SpringMVCContract 服务接口解析
- RequestParamParameterProcessor 参数解析
- feign.RequestTemplate Rest请求构造器
- feign.Request 处理http请求
=========================================================

关注公众号,阅读更多文章。
SpringCloud OpenFeign Post请求的坑的更多相关文章
- 【BIGDATA】ElasticSearch HEAD插件的GET请求的坑
今使用HEAD插件,发现复杂查询功能下,使用GET请求有坑. 查询语句如下: GET kk/_search { "query": { "match": { &q ...
- Spring-cloud (九) Hystrix请求合并的使用
前言: 承接上一篇文章,两文本来可以一起写的,但是发现RestTemplate使用普通的调用返回包装类型会出现一些问题,也正是这个问题,两文没有合成一文,本文篇幅不会太长,会说一下使用和适应的场景. ...
- vue前端post请求之坑
最近用的vue请求数据,坑死,还是对前端vue框架不熟. 与后端通信有问题,要么是json加入到url有问题.要么是json解析有问题. 解决方法: 1.请求参数一个用url传 var json=[{ ...
- 高并发场景-请求合并(一)SpringCloud中Hystrix请求合并
背景 在互联网的高并发场景下,请求会非常多,但是数据库连接池比较少,或者说需要减少CPU压力,减少处理逻辑的,需要把单个查询,用某些手段,改为批量查询多个后返回. 如:支付宝中,查询"个人信 ...
- SpringCloud gateway自定义请求的 httpClient
本文为博主原创,转载请注明出处: 引用 的 spring cloud gateway 的版本为 2.2.5 : SpringCloud gateway 在实现服务路由并请求的具体过程是在 org.sp ...
- datatables ajax后端请求那些坑
在对datatables做后端数据填充的时候,遇到一个,翻页问题.在多次操作翻页后,总是提示加载中.反了很多博客没有找到原因. 经过测试,发现原来自己坑了自己. 代码如下: datatables : ...
- SpringCloud实战-Hystrix请求熔断与服务降级
我们知道大量请求会阻塞在Tomcat服务器上,影响其它整个服务.在复杂的分布式架构的应用程序有很多的依赖,都会不可避免地在某些时候失败.高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险 ...
- Spring-cloud (八) Hystrix 请求缓存的使用
前言: 最近忙着微服务项目的开发,脱更了半个月多,今天项目的初版已经完成,所以打算继续我们的微服务学习,由于Hystrix这一块东西好多,只好多拆分几篇文章写,对于一般对性能要求不是很高的项目中,可以 ...
- 注意:Tomcat Get请求的坑!
Tomcat8.5,当Get请求中包含了未经编码的中文字符时,会报以下错误,请求未到应用程序在Tomcat层就被拦截了. Tomcat报错: java.lang.IllegalArgumentExce ...
随机推荐
- 虚拟化技术之kvm基础
一.KVM简介 KVM的全称是kernel base virtual machine(基于内核的虚拟机)是一个开源的系统虚拟化模块,自Linux 2.6.20之后集成在Linux的各个主要发行版本中. ...
- int ,long , long long , __int64类型的范围
首先见测试代码(在g++/gcc下运行): #include<iostream> using namespace std; int main() { cout<<sizeof( ...
- python 07 字典 集合
字典 key:value 键:值 映射,哈希值,关系(数组) x=['1','2'] y=['a','b'] >>print(y[x.index('1')]) a index() 函数用于 ...
- 使用folderLeft函数统计字母出现的次数
实例:统计字符串中字母出现的次数 import scala.collection.mutable object Demo_018{ def main(args: Array[String]): Uni ...
- MySql安装后在服务管理器里边找不到MySql服务项的解决办法(win10)
问题描述: 成功安装MySql后,使用mysql的时候,在CMD中输入net start mysql,提示服务名无效,查看服务列表也找不到mysql服务. 解决办法: 首先用管理员身份打开CMD命令, ...
- JDBC | 第一章: 快速开始使用JDBC连接Mysql数据库?
开始使用基于java的JDBC技术来连接mysql进行msyql数据库简单的CRUD操作 下载对应mysql驱动包 这里我创建maven项目基于maven下载 <!--mysql 驱动--> ...
- JavaScript学习系列博客_18_JavaScript中的匿名函数
匿名函数 - 用函数声明的方式创建一个函数时,不加函数名称. function sum(){ console.log("我是函数sum")} - 不加名称,这样写浏览器是会报错的. ...
- powerdesigner16.5 生成sql语句
1.首先安装好designer. 2.打开之后新建一个project. 3.创建之后可能会生成这样的页面. 4.点击这个图标就能调出那个视图框. 5.在project处单机右键,New一个Concep ...
- 算法-搜索(4)ISAM算法
ISAM技术是一种典型的多叉搜索树结构,它使用了3级索引结构:主索引.柱面索引.磁道索引 所有数据记录在基本区按关键码升序排序,后一磁道所有关键码均大于前一磁道.在某一磁道插入新记录时,如果原来该磁道 ...
- A review of learning in biologically plausible spiking neural networks
郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! Contents: ABSTRACT 1. Introduction 2. Biological background 2.1. Spik ...