在微服務環境下,遠程調用feign和異步線程存在請求數據丟失問題


一、無異步線程得情況下feign遠程調用:

0、登錄攔截器:

@Component
public class LoginUserInterceptor implements HandlerInterceptor {
    public static ThreadLocal<MemberResVo> loginUser = new ThreadLocal<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //獲取登錄用戶的鍵
        MemberResVo attribute = (MemberResVo) request.getSession().getAttribute(AuthServerConstant.LONG_USER);
        if (attribute!=null){
            loginUser.set(attribute);
            return true;
        }else {
            request.getSession().setAttribute("msg","請先進行登錄!");
            response.sendRedirect("http://auth.gulimall.com/login.html");
            return false;
        }
    }
}

1、問題示例圖:

 解決方法:

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;

@Configuration
public class GuliFeignConfig {
    //fegin過濾器
    @Bean("requestInterceptor")
    public RequestInterceptor requestInterceptor() {
        return new RequestInterceptor() {
            public void apply(RequestTemplate template) {
                //上下文環境保持器,拿到剛進來這個請求包含的數據,而不會因為遠程數據請求頭被清除
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();//老的請求
                if (request != null) {
                    //同步老的請求頭中的數據,這里是獲取cookie
                    String cookie = request.getHeader("Cookie");
                    template.header("Cookie", cookie);
                }
            }
        };
    }
}

二、異步情況下丟失上下文問題:

① 在同一線程下進行遠程調用,即一連串調用的情況下OrederService通過遠程調用先查找adress信息,再查找cart信息,則僅需配置GuliFeignConfig就夠了

② 由於采用的異步任務,所以101、102線程在自己的線程中調用登錄攔截器interceptor,而其實只有在72號線程中登陸攔截器才進行放行(有請求頭數據),這就導致101、102的request為null

解決方式(高亮部分):從總線中獲取request數據放入子線程中

@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeginService cartFeginService;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    WmsFeignService wmsFeignService;

    /**
     * 訂單確認頁返回的數據
     * @return
     */
    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        MemberResVo memberResVo = LoginUserInterceptor.loginUser.get();
        //從主線程中獲得所有request數據
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
            //1、遠程查詢所有地址列表
 RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> address = memberFeignService.getAddress(memberResVo.getId());

            confirmVo.setAddress(address);
        }, executor);

        //2、遠程查詢購物車所選的購物項,獲得所有購物項數據
        CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
            //放入子線程中request數據
 RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> items = cartFeginService.getCurrentUserCartItems();
            confirmVo.setItem(items);
        }, executor).thenRunAsync(()->{
           RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> items = confirmVo.getItem();
            List<Long> collect = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
            //遠程調用查詢是否有庫存
            R hasStock = wmsFeignService.getSkusHasStock(collect);
            //形成一個List集合,獲取所有物品是否有貨的情況
            List<SkuStockVo> data = hasStock.getData(new TypeReference<List<SkuStockVo>>() {
            });
            if (data!=null){
                //收集起來,Map<Long,Boolean> stocks;
                Map<Long, Boolean> map = data.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock));
                confirmVo.setStocks(map);
            }
        },executor);
        //feign遠程調用在調用之前會調用很多攔截器,因此遠程調用會丟失很多請求頭

        //3、查詢用戶積分
        Integer integration = memberResVo.getIntegration();
        confirmVo.setIntegration(integration);
        //其他數據自動計算

        CompletableFuture.allOf(getAddressFuture,cartFuture).get();
        return confirmVo;
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM