一、無異步線程得情況下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; } }