SpringCloud OpenFeign Post请求出现400错误的解决方法
创始人
2024-11-16 02:23:16
0次

问题发现与分析

最近线上服务器突然经常性出现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上,如下图:

SpringCloud OpenFeign Post请求出现400错误的解决方法

正是因为这个巨坑,导致了线上服务器经常性高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上了

SpringCloud OpenFeign Post请求出现400错误的解决方法

@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) {
}

最终调用日志

SpringCloud OpenFeign Post请求出现400错误的解决方法

涉及的Java类

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

  • SpringMVCContract 服务接口
  • RequestParamParameterProcessor 参数
  • feign.RequestTemplate Rest请求构造器
  • feign.Request 处理http请求

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...