SpringCloud微服務調(diào)用丟失請求頭的問題及解決方案
在 Spring Cloud 中 微服務之間的調(diào)用會用到Feign,但是在默認情況下,F(xiàn)eign 調(diào)用遠程服務存在Header請求頭丟失問題。但基本上每個服務都會有一個全局globalId,能夠清除調(diào)用鏈路,可以有兩種解決方案
解決方案一
可以在每次遠程調(diào)用時,使用@RequestHeader注解重新封裝請求頭
@GetMapping("/test")
String test(String res, @RequestHeader String globalId);解決方案二
可以使用springcloud提供的feign攔截器RequestInterceptor,攔截請求頭重新進行封裝
package com.test.feignheader.config;
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;
/**
* @Description: feign攔截器功能, 解決header丟失問題
**/
@Configuration
public class FeignConfig {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor() {
RequestInterceptor requestInterceptor = new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
//1、使用RequestContextHolder拿到剛進來的請求數(shù)據(jù)
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
//老請求
HttpServletRequest request = requestAttributes.getRequest();
if (request != null) {
//2、同步請求頭的數(shù)據(jù)(主要是cookie)
//把老請求的cookie值放到新請求上來,進行一個同步
String cookie = request.getHeader("Cookie");
template.header("Cookie", cookie);
}
}
}
};
return requestInterceptor;
}
}也可以參考下面的代碼進行封裝
public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {
private static final Logger logger = LoggerFactory.getLogger(FeignBasicAuthRequestInterceptor.class);
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
//跳過 content-length,避免追加參數(shù)導致content-length值與實際長度不一樣
if (name.equals("content-length")) {
continue;
}
requestTemplate.header(name, values);
}
}
/** body一般不需要處理,否則會導致微服務調(diào)用異常
Enumeration<String> bodyNames = request.getParameterNames();
StringBuffer body =new StringBuffer();
if (bodyNames != null) {
while (bodyNames.hasMoreElements()) {
String name = bodyNames.nextElement();
String values = request.getParameter(name);
body.append(name).append("=").append(values).append("&");
}
}
if(body.length()!=0) {
body.deleteCharAt(body.length()-1);
requestTemplate.body(body.toString());
logger.info("feign interceptor body:{}",body.toString());
}
*/
}
}配置 讓所有 FeignClient,使用 FeignBasicAuthRequestInterceptor
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
requestInterceptors: com.leparts.config.FeignBasicAuthRequestInterceptor也可以配置讓 某個 FeignClient 使用這個 FeignBasicAuthRequestInterceptor
feign:
client:
config:
xxxx: # 遠程服務名
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
requestInterceptors: com.leparts.config.FeignBasicAuthRequestInterceptor經(jīng)過測試,上面的解決方案可以正常的使用;但是出現(xiàn)了新的問題。
在轉(zhuǎn)發(fā)Feign的請求頭的時候, 如果開啟了Hystrix, Hystrix的默認隔離策略是Thread(線程隔離策略), 因此轉(zhuǎn)發(fā)攔截器內(nèi)是無法獲取到請求的請求頭信息的。
可以修改默認隔離策略為信號量模式:
hystrix.command.default.execution.isolation.strategy=SEMAPHORE
但信號量模式不是官方推薦的隔離策略;另一個解決方法就是自定義Hystrix的隔離策略。
自定義策略
HystrixConcurrencyStrategy 是提供給開發(fā)者去自定義hystrix內(nèi)部線程池及其隊列,還提供了包裝callable的方法,以及傳遞上下文變量的方法。所以可以繼承了HystrixConcurrencyStrategy,用來實現(xiàn)了自己的并發(fā)策略。
@Component
public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private static final Logger log = LoggerFactory.getLogger(FeignHystrixConcurrencyStrategy.class);
private HystrixConcurrencyStrategy delegate;
public FeignHystrixConcurrencyStrategy() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof FeignHystrixConcurrencyStrategy) {
// Welcome to singleton hell...
return;
}
HystrixCommandExecutionHook commandExecutionHook =
HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy =
HystrixPlugins.getInstance().getPropertiesStrategy();
this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy);
HystrixPlugins.reset();
HystrixPlugins instance = HystrixPlugins.getInstance();
instance.registerConcurrencyStrategy(this);
instance.registerCommandExecutionHook(commandExecutionHook);
instance.registerEventNotifier(eventNotifier);
instance.registerMetricsPublisher(metricsPublisher);
instance.registerPropertiesStrategy(propertiesStrategy);
} catch (Exception e) {
log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
}
}
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher,
HystrixPropertiesStrategy propertiesStrategy) {
if (log.isDebugEnabled()) {
log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
+ this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
+ metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new WrappedCallable<>(callable, requestAttributes);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize,
HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
unit, workQueue);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixThreadPoolProperties threadPoolProperties) {
return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);
}
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
return this.delegate.getRequestVariable(rv);
}
static class WrappedCallable<T> implements Callable<T> {
private final Callable<T> target;
private final RequestAttributes requestAttributes;
WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
this.target = target;
this.requestAttributes = requestAttributes;
}
@Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return target.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}致此,F(xiàn)eign調(diào)用丟失請求頭的問題就解決的了 。
到此這篇關(guān)于SpringCloud微服務調(diào)用丟失請求頭的文章就介紹到這了,更多相關(guān)SpringCloud微服務調(diào)用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解JDK自帶javap命令反編譯class文件和Jad反編譯class文件(推薦使用jad)
這篇文章主要介紹了JDK自帶javap命令反編譯class文件和Jad反編譯class文件(推薦使用jad),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09
IntelliJ IDEA快速創(chuàng)建getter和setter方法
這篇文章主要介紹了IntelliJ IDEA快速創(chuàng)建getter和setter方法,本文通過圖文實例相結(jié)合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03
classloader類加載器_基于java類的加載方式詳解
下面小編就為大家?guī)硪黄猚lassloader類加載器_基于java類的加載方式詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
Spring Cloud Gateway調(diào)用Feign異步問題記錄
這篇文章主要介紹了Spring Cloud Gateway調(diào)用Feign異步問題記錄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
Java單元測試Powermockito和Mockito使用總結(jié)
公司單元測試框架選用了Junit 4.12,Mock框架選用了Mockito和PowerMock,本文主要介紹了Java單元測試Powermockito和Mockito使用總結(jié),感興趣的可以了解一下2021-09-09

