Feign远程调用问题

1.Feign远程调用丢失请求头问题

以如下代码为例:

@FeignClient("gulimall-member")
public interface MemberFeignService {

    @GetMapping("member/memberreceiveaddress/{memberId}/address")
    List<MemberAddressVo> getAddress(@PathVariable("memberId") Long memberId);
}


F7断点进入ReflectiveFeign.java:

F7断点进入SynchronousMethodHandler.java:

F7断点进入executeAndDecode方法:

再进入targetRequest方法,发现RequestInterceptors是0个:

解决方案:如果我们只有注入了RequestInterceptor拦截器,我们就可以给容器中的RequestTemplate添加请求头信息了

代码:在容器中注入RequestInterceptor(feign的请求拦截器)

@Configuration
public class FeignConfig {

    @Bean
    public RequestInterceptor requestInterceptor() {
        return template -> {
            //1、RequestContextHolder拿到刚进来的这个请求
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest(); //老请求
                //同步请求头数据 Cookie
                String cookie = request.getHeader("Cookie");
                //给新请求同步了老请求的cookie
                template.header("Cookie", cookie);
                System.out.println("feign远程之前先进行RequestInterceptor.apply");
            }
        };
    }
}

结论:

2.Feign远程调用异步情况丢失上下文问题

基于1.的feign请求拦截器配置
以如下确认订单代码为例:

        OrderConfirmVo confirmVo = new OrderConfirmVo();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        System.out.println("主线程..." + Thread.currentThread().getId());

        CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
            System.out.println("member线程..." + Thread.currentThread().getId());
            //1、远程查询所有的收获地址列表
            List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
            confirmVo.setAddress(address);
        }, executor);

        CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
            System.out.println("cart线程..." + Thread.currentThread().getId());
            //2、远程查询购物车所有选中的购物项
            List<OrderItemVo> items = cartFeignService.getCurrentUserCartItems();
            confirmVo.setItems(items);
            //feign在远程调用之前要构造请求,调用很多的拦截器
            //RequestInterceptor interceptor: requestInterceptors
        }, executor);

        //3、查询用户积分
        Integer integration = memberRespVo.getIntegration();
        confirmVo.setIntegration(integration);

        //4、其他数据自动计算

        //TODO 5、防重令牌
        CompletableFuture.allOf(getAddressFuture, cartFuture).get();

主线程运行:

两个feign异步线程与主线程不是同一线程,导致两个feign异步线程ThreadLocal中的请求头信息为空,从而出现异步线程丢失了上文的问题

解决方案:
在异步线程执行之前的主线程中获取之前的请求:

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

然后在异步线程中的feign调用之前,设置之前的请求数据

//每一个线程都来共享之前的请求数据
RequestContextHolder.setRequestAttributes(requestAttributes);

结论:

posted @ 2022-11-20 17:35  冰枫丶  阅读(129)  评论(0编辑  收藏  举报