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

在使用Feign进行远程调用时,当前服务是能拿到请求头信息的,请求头包含有登录认证Cookie等重要信息,但是在调用远程服务时,远程服务却拿不到请求头信息,因为使用Feign进行远程调用实际上是发起新的Request请求了,所以就获取不到上一个请求的数据。请求示意图如下所示:

此时我们需要在在当前服务中添加配置类,用于将当前请求信息传递给远程服务,下面是配置类示例代码:

java 复制代码
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @author: 
 * @Desc:
 * @create:
 **/
@Configuration
public class GuliFeignConfig {
    @Bean("requestInterceptor")
    public RequestInterceptor requestInterceptor(){
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
                //RequestContextHolder是feign设计用于获取请求上下文数据的工具类
               ServletRequestAttributes requestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = requestAttributes.getRequest();
                if(request!=null){
                    //获取源请求的请求头数据
                    String cookie = request.getHeader("Cookie");
                    //给新请求同步源请求的Cookie
                    template.header("Cookie",cookie);
                }

            }
        };
    }
}

添加上面的配置类后,在同步环境中能可确保远程服务能获取当前服务的请求头数据。但是如果使用异步操作,仅靠上面的配置类是不够的。这是因为:

数据是线程私有的,不能共享,所以使用异步的远程服务获取不了主线程的请求数据。此时需要进行配置:

方法1:硬编码,在主线程中获取到请求数据,在异步线程操作中赋值进去,

优点:简单易懂,上手快;

缺点:侵入业务代码,且每使用一个线程就要写这样一段代码,还会导致大量冗余代码。

示例代码:

java 复制代码
         //获取当前线程请求数据
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //使用异步编排处理多个远程请求任务
        //1.远程查询会员的收货地址
        CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
            //使用异步编排后,由于线程不同,需要将源请求上下文数据赋给当前线程
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> address = orderFeignServie.getAddress(memberRespVo.getId());
            orderConfirmVo.setAddress(address);
        }, executor);

        //2.远程查询购物车中的购物项
        CompletableFuture<Void> getCartItemFuture = CompletableFuture.runAsync(() -> {
            //使用异步编排后,由于线程不同,需要将请求源上下文数据赋给当前线程
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> orderItemVos = cartFeignService.currentUserCartItem();
            orderConfirmVo.setItems(orderItemVos);
        }, executor);

方法2: 创建一个抽象类,实现Runnable方法。

优点:无侵入,代码简洁;

下面是示例代码:

java 复制代码
public abstract class MyRunnable implements Runnable{
    private RequestAttributes requestAttributes;

    public MyRunnable() {
        requestAttributes = RequestContextHolder.getRequestAttributes();
    }

    public abstract void myRun();

    @Override
    public void run() {
        //每一个线程都来共享之前的请求数据
        RequestContextHolder.setRequestAttributes(requestAttributes);
        myRun();
    }
}

然后在业务代码中使用MyRunnable的myRun()方法:

java 复制代码
//开启第一个异步任务
CompletableFuture<Void> addressFuture2 = CompletableFuture.runAsync(new MyRunnable() {
	@Override
	public void myRun() {
		//1、远程查询所有的收获地址列表
		List<MemberAddressVo> address = memberFeignService.getAddress(memberResponseVo.getId());
		confirmVo.setMemberAddressVos(address);
	}
}, threadPoolExecutor);

如此便可在异步使用Feign进行远程调用时获取到当前请求的请求数据。

更多信息可参考:谷粒商城实战笔记-问题记录-Feign异步调用丢失请求头问题_异步调用feign请求对象丢失-CSDN博客

相关推荐
sduwcgg1 小时前
IQ-Learn 在 RTX 3090 服务器上的环境配置与踩坑记录
运维·服务器
QFIUNE1 小时前
CD-HIT 详解:序列去冗余、安装使用与聚类结果解析
linux·服务器·机器学习·数据挖掘·conda·聚类
marsh02062 小时前
43 openclaw熔断与降级:保障系统在异常情况下的可用性
java·运维·网络·ai·编程·技术
摇滚侠2 小时前
Docker 如何查询挂载的目录
运维·docker·容器
勇闯逆流河3 小时前
【Linux】linux进程控制(进程池的详解与实现)
linux·运维·服务器
zhangfeng11333 小时前
部署到服务器上 宝塔系统 使用宝塔在线编辑器 FTP 批量上传 Git 部署 打包上传 codebudyy 编程程序开发
服务器·git·编辑器
WJ.Polar4 小时前
Scapy基本应用
linux·运维·网络·python
lljss20204 小时前
1. NameServer 域名服务器---NS
linux·服务器·前端
数智顾问5 小时前
(123页PPT)华为流程管理体系精髓提炼(附下载方式)
运维·华为
网络工程小王6 小时前
【LCEL 链式调用详解】调用篇-2
java·服务器·前端·数据库·人工智能