解決在微服務環(huán)境下遠程調(diào)用feign和異步線程存在請求數(shù)據(jù)丟失問題
一、無異步線程得情況下feign遠程調(diào)用:
1、登錄攔截器:
@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;
}
}
}2.問題示例圖:

3.解決方法:
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) {
//上下文環(huán)境保持器,拿到剛進來這個請求包含的數(shù)據(jù),而不會因為遠程數(shù)據(jù)請求頭被清除
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();//老的請求
if (request != null) {
//同步老的請求頭中的數(shù)據(jù),這里是獲取cookie
String cookie = request.getHeader("Cookie");
template.header("Cookie", cookie);
}
}
};
}
}二、異步情況下丟失上下文問題:

① 在同一線程下進行遠程調(diào)用,即一連串調(diào)用的情況下OrederService通過遠程調(diào)用先查找adress信息,再查找cart信息,則僅需配置GuliFeignConfig就夠了
② 由于采用的異步任務,所以101、102線程在自己的線程中調(diào)用登錄攔截器interceptor,而其實只有在72號線程中登陸攔截器才進行放行(有請求頭數(shù)據(jù)),這就導致101、102的request為null
解決方式(高亮部分):從總線中獲取request數(shù)據(jù)放入子線程中
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes()
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity>
implements OrderService {
@Autowired
MemberFeignService memberFeignService;
@Autowired
CartFeginService cartFeginService;
@Autowired
ThreadPoolExecutor executor;
@Autowired
WmsFeignService wmsFeignService;
/**
* 訂單確認頁返回的數(shù)據(jù)
* @return
*/
@Override
public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
OrderConfirmVo confirmVo = new OrderConfirmVo();
MemberResVo memberResVo = LoginUserInterceptor.loginUser.get();
//從主線程中獲得所有request數(shù)據(jù)
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、遠程查詢購物車所選的購物項,獲得所有購物項數(shù)據(jù)
CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
//放入子線程中request數(shù)據(jù)
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());
//遠程調(diào)用查詢是否有庫存
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遠程調(diào)用在調(diào)用之前會調(diào)用很多攔截器,因此遠程調(diào)用會丟失很多請求頭
//3、查詢用戶積分
Integer integration = memberResVo.getIntegration();
confirmVo.setIntegration(integration);
//其他數(shù)據(jù)自動計算
CompletableFuture.allOf(getAddressFuture,cartFuture).get();
return confirmVo;
}
}
到此這篇關于解決在微服務環(huán)境下遠程調(diào)用feign和異步線程存在請求數(shù)據(jù)丟失問題的文章就介紹到這了,更多相關feign遠程調(diào)用請求丟失內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring配置動態(tài)數(shù)據(jù)源實現(xiàn)讀寫分離的方法
這篇文章主要介紹了利用Spring配置動態(tài)數(shù)據(jù)源實現(xiàn)讀寫分離的方法,文中通過示例代碼介紹的很詳細,相信對大家的理解和學習具有一定的參考借鑒價值,藕需要的朋友可以一起學習學習。2017-01-01
Java HashSet(散列集),HashMap(散列映射)的簡單介紹
這篇文章主要介紹了Java HashSet(散列集),HashMap(散列映射)的簡單介紹,幫助大家更好的理解和學習Java集合框架的相關知識,感興趣的朋友可以了解下2021-01-01
Java解析Excel文件并把數(shù)據(jù)存入數(shù)據(jù)庫
本篇文章主要介紹了Java解析Excel文件并把數(shù)據(jù)存入數(shù)據(jù)庫 ,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05
Java編程實現(xiàn)統(tǒng)計一個字符串中各個字符出現(xiàn)次數(shù)的方法
這篇文章主要介紹了Java編程實現(xiàn)統(tǒng)計一個字符串中各個字符出現(xiàn)次數(shù)的方法,涉及java針對字符串的遍歷、判斷、運算等相關操作技巧,需要的朋友可以參考下2017-12-12

