关于Feign的一些问题

1.feign远程调用会丢失请求头信息

有时候feign远程调用发现接口方法中调用了获取session数据结果为null,检查来检查去发现是请求头中的Cookie丢失了,也就没有sessionId(jessionId),发送请求时没携带cookie,找不到服务器中session数据。原因是Feign调用会创建新的请求,请求模板里没有初始请求携带的头信息,需要添加拦截器增强feign,即添加拦截器用来同步初始请求头信息到feign请求模板去。原因是代理创建了新的请求,不再是初始的请求,需要添加拦截器增强feign,即添加一个拦截器来同步初始请求的请求头信息,核心原理是利用ThreadLocal同线程变量共享。

package com.atguigu.gulimall.order.config;

import feign.RequestInterceptor;
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;
import java.util.Objects;

/**
 * User: ldj
 * Date: 2022/10/12
 * Time: 21:35
 * Description:
 * feign远程调用会丢失请求头信息,原因是代理创建了新的请求,不再是初始的请求,
 * 需要添加拦截器增强feign,即添加一个拦截器来同步初始请求的请求头信息
 */

@Configuration
public class MyFeignConfig {

    @Bean
    public RequestInterceptor requestInterceptor() {
        return template -> {
            //获取到最初请求的参数
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest initiallyRequest = Objects.requireNonNull(attributes).getRequest();
            //新请求同步了最初请求头数据的Cookie
            String cookie = initiallyRequest.getHeader("Cookie");
            template.header("Cookie", cookie);
        };
    }
}
2.feign异步调用会丢失请求上下文信息

结合上面,ThreadLocal同线程变量共享,异步走不再是同一条线程,异步脱离主线程,不同线程之间ThreadLocal无法再共享数据。解决方法是在走分支线程前,将主线程之前的请求数据同步到分支线程

//示例代码,CompletableFuture做异步编排(多线程方式)

/**
     * 1.涉及复杂多路查询,需要使用线程池方式实现异步调用方案
     * 2.feign异步调用会丢失请求上下文信息,原因是异步脱离主线程,不同线程ThreadLocal无法共享数据
     * 解决方法是在走分支线程前,将主线程的数据同步到分支线程
     */
    @Override
    public OrderConfirmVO getConfirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVO orderConfirmVO = new OrderConfirmVO();
        MemberRespVO memberRespVO = LoginUserInterceptor.threadLocal.get();
        System.out.println("主线程=====>" + Thread.currentThread().getId());

        //主线程请求数据
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        //1.远程查询用户的收获地址列表,选用默认收获地址,会员id从拦截器获取
        CompletableFuture<Void> getHarvestAddressFuture = CompletableFuture.runAsync(() -> {
            System.out.println("分支线程1=====>" + Thread.currentThread().getId());
            //同步主线程的数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVO> harvestAddress = memberFeignService.getHarvestAddress(memberRespVO.getId());
            orderConfirmVO.setAddress(harvestAddress);
        }, executor);

        //2.远程查询购物车选中的购物项
        CompletableFuture<Void> getChosenCartItemsFuture = CompletableFuture.runAsync(() -> {
            System.out.println("分支线程2=====>" + Thread.currentThread().getId());
            List<OrderItemVO> chosenCartItems = cartFeignService.getChosenCartItems();
            orderConfirmVO.setItems(chosenCartItems);
        }, executor);

        //阻塞等待所有线程完成
        CompletableFuture.allOf(getHarvestAddressFuture, getChosenCartItemsFuture).get();

        return orderConfirmVO;
    }

说明:1和2其实是一个意思

3.本地事务注解 @Transactional不能控制远程服务事务回滚,最后会导致数据不一致

 例子:假设有一个下单操作,步骤是确认订单后,先保存订单,后Feign远程调用接扣库存会出现可能情况

1.远程调用接口扣库存,如果出现网络抖动,抛出超时异常,保存订单会回滚,但是扣库存是成功;
2.一旦远程调用接口扣库存成功,后面代码再抛异常,扣库存无法回滚。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值