Feign:远程和异步调用丢失头信息的问题

本文详细探讨了Feign在远程调用时如何处理请求头丢失问题,包括如何通过RequestInterceptor在同步和异步调用中保持Cookie一致性,以确保购物车服务正确识别用户身份。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

示例以谷粒商城订单系统远程调用购物车系统为例!!!

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

在这里插入图片描述

Feign在远程调用之前要构造请求 调用很多拦截器,

订单服务的ToTrade请求带了cookie等头信息,但是要进行远程调用时,Feign创建了一个新的请求,这个新请求里面没有任何头信息,所以会丢失请求头;导致购物车服务认为这个请求没登录;

解决:

加上feign远程调用的请求拦截器RequestInterceptor,会首先调用请求拦截器的apply()方法,在apply()方法里面通过RequestContextHolder拿到刚进来这个请求,然后给新请求同步了老请求的Cookie;

新请求是RequestTemplate,老请求是HttpServletRequest;

package com.atguigu.gulimall.order.config;

@Configuration
public class OrderFeignConfig {

	@Bean("requestInterceptor")
	public RequestInterceptor requestInterceptor(){
		// Feign在远程调用之前都会先经过这个方法
		return new RequestInterceptor() {
			@Override
			public void apply(RequestTemplate template) {
				// RequestContextHolder拿到刚进来这个请求
				ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
				if(attributes != null){
					HttpServletRequest request = attributes.getRequest();
					if(request != null){
						// 同步请求头数据
						String cookie = request.getHeader("Cookie");
						// RequestTemplate template是新请求,给新请求同步Cookie
						template.header("Cookie", cookie);
					}
				}
			}
		};
	}
}

Feign异步调用丢失header请求头的问题

在这里插入图片描述

因为异步线程需要新的线程,而新的线程里没有request数据,所以我们自己设置进去

将主线程的header数据又再次手动赋值给子线程;

RequestContextHolder里面是使用ThreadLocal来存储RequestAttributes的,所以异步情况下拿不到

(1)先拿到原请求数据:RequestAttributes attributes = RequestContextHolder.getRequestAttributes();

(2)再在每个子线程进来时把数据设置进去:RequestContextHolder.setRequestAttributes(attributes);

@Override // OrderServiceImpl
public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
    // 获取用户,用用户信息获取购物车
    MemberRespVo memberRespVo = LoginUserInterceptor.threadLocal.get();
    // 封装订单
    OrderConfirmVo confirmVo = new OrderConfirmVo();
    // 我们要从request里获取用户数据,但是其他线程是没有这个信息的,
    // 所以可以手动设置新线程里也能共享当前的request数据
    RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
    // 1.远程查询所有的收货地址列表
    CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
      // 因为异步线程需要新的线程,而新的线程里没有request数据,所以我们自己设置进去
        RequestContextHolder.setRequestAttributes(attributes);
        List<MemberAddressVo> address;
        try {
            address = memberFeignService.getAddress(memberRespVo.getId());
            confirmVo.setAddress(address);
        } catch (Exception e) {
            log.warn("\n远程调用会员服务失败 [会员服务可能未启动]");
        }
    }, executor);

    // 2. 远程查询购物车服务,并得到每个购物项是否有库存
    CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
        // 异步线程共享 RequestContextHolder.getRequestAttributes()
        RequestContextHolder.setRequestAttributes(attributes);
        // feign在远程调用之前要构造请求 调用很多拦截器
        // 远程获取用户的购物项
        List<OrderItemVo> items = cartFeignService.getCurrentUserCartItems();
        confirmVo.setItems(items);
    }, executor).thenRunAsync(() -> {
        RequestContextHolder.setRequestAttributes(attributes);
        List<OrderItemVo> items = confirmVo.getItems();
        // 获取所有商品的id
        List<Long> skus = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
        R hasStock = wmsFeignService.getSkuHasStock(skus);
        List<SkuStockVo> data = hasStock.getData(new TypeReference<List<SkuStockVo>>() {});
        if (data != null) {
            // 各个商品id 与 他们库存状态的映射map // 学习下收集成map的用法
            Map<Long, Boolean> stocks = data.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock));
            confirmVo.setStocks(stocks);
        }
    }, executor);

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

    // 4.其他数据在类内部自动计算

    // TODO 5.防重令牌 设置用户的令牌
    String token = UUID.randomUUID().toString().replace("-", "");
    confirmVo.setOrderToken(token);
    // redis中添加用户id,这个设置可以防止订单重复提交。生成完一次订单后删除redis
    stringRedisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId(), token, 10, TimeUnit.MINUTES);
    // 等待所有异步任务完成
    CompletableFuture.allOf(getAddressFuture, cartFuture).get();
    return confirmVo;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值