OpenFeign异步线程调用丢失请求头问题排查记录

前言

事情的起因是因为我们在业务的日志中发现偶尔会出现一个这样的报错,而且正是因为这样的一个报错,导致我们一些用户的权益下发失败。
丢失请求头

分析

看这个报错提示,能看出来是content-type请求时没带上。我们项目中使用的是openFeign进行微服务调用,那为什么会没有content-type呢?
查看代码观察到,对于的报错代码都使用了线程池进行异步业务处理,主线程则立即返回,线程池中线程调用时产生了如下的报错。

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>

寻找解决途径

知道表象后我开始查阅百度谷歌资料…

直接github提个issue

issue地址
当时提了一个issue,那时候报错表象可能不一样,但究其原因是丢失了请求头content-type。但大佬们回答的效率确实不高,还建议我使用缺省的content-type。

本地搭建服务测试

发现服务能够正常的调用,但是当将feign异步时,主线程提前完成则失败,主线程等待则成功。

@RestController
public class TestController {

    @Autowired
    private AsyncFeignClient asyncFeignClient;
    
    @PostMapping("/async")
    public String async() throws InterruptedException {
        Thread thread = new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
           asyncFeignClient.postReq(new Model(),"123");
        });
        thread.start();
        thread.join();

        return "success";
    }
}

@FeignClient(url = "127.0.0.1:9001",name = "async-feign",contextId = "identity")
public interface AsyncFeignClient {

    @PostMapping(value = "/transport")
    String postReq(@RequestBody Model mobile, @RequestHeader("token") String token);
}

去掉 thread.join() 服务端则会出现如下报错
在这里插入图片描述

源码debug

于是,借着这次机会再捋一下老熟人feignclient请求的流程。具体的流程太长,另起一篇记录。
如下是关键点

一开始进入feign调用后其实调用的是代理类的invoke方法,并且由于声明了请求头和@RequestBody所以template生成时有如下的头部
在这里插入图片描述
再往下走 executeAndDecode方法
在这里插入图片描述
在这里targetRequest之后头就被清空了,进去里面看到其实是自定义了拦截器导致
在这里插入图片描述

所以年轻人,自定义拦截器一定要小心。

也正是因为拦截器中的下面代码对template头部进行了清空 而究其原因就是在主线程结束后异步线程并没有上下问但他却取到了下面的request。
在这里插入图片描述
在这里插入图片描述
说来也奇怪为什么这里传空就不是追加而是清除还不是很理解。

到这里排查就结束了 ?

另外原因

其实这个问题还有一个原因,我的同事在误以为为什么无法传递头部的时候使用了如下代码

RequestContextHolder.setRequestAttributes(requestAttributes);

很可能参考了一些博客的做法
在这里插入图片描述

但人家是等待所有异步线程结束才返回,而这里的业务是直接返回,显然里面的头也就没有了也没有任何作用,并且导致了取到了空对象的上下文。
同时,也需要排查下框架中有无全局设置RequestContextHolder.setRequestAttributes(requestAttributes,true);的地方,因为这个的作用等同于在异步线程设置requestAttributes

结束

所以看问题 早看源码早胜利 加油

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值