SpringCloud微服務(wù)調(diào)用丟失請(qǐng)求頭的問題及解決方案
在 Spring Cloud 中 微服務(wù)之間的調(diào)用會(huì)用到Feign,但是在默認(rèn)情況下,F(xiàn)eign 調(diào)用遠(yuǎn)程服務(wù)存在Header請(qǐng)求頭丟失問題。但基本上每個(gè)服務(wù)都會(huì)有一個(gè)全局globalId,能夠清除調(diào)用鏈路,可以有兩種解決方案
解決方案一
可以在每次遠(yuǎn)程調(diào)用時(shí),使用@RequestHeader注解重新封裝請(qǐng)求頭
@GetMapping("/test")
String test(String res, @RequestHeader String globalId);解決方案二
可以使用springcloud提供的feign攔截器RequestInterceptor,攔截請(qǐng)求頭重新進(jìn)行封裝
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拿到剛進(jìn)來的請(qǐng)求數(shù)據(jù)
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
//老請(qǐng)求
HttpServletRequest request = requestAttributes.getRequest();
if (request != null) {
//2、同步請(qǐng)求頭的數(shù)據(jù)(主要是cookie)
//把老請(qǐng)求的cookie值放到新請(qǐng)求上來,進(jìn)行一個(gè)同步
String cookie = request.getHeader("Cookie");
template.header("Cookie", cookie);
}
}
}
};
return requestInterceptor;
}
}也可以參考下面的代碼進(jìn)行封裝
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ù)導(dǎo)致content-length值與實(shí)際長(zhǎng)度不一樣
if (name.equals("content-length")) {
continue;
}
requestTemplate.header(name, values);
}
}
/** body一般不需要處理,否則會(huì)導(dǎo)致微服務(wù)調(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也可以配置讓 某個(gè) FeignClient 使用這個(gè) FeignBasicAuthRequestInterceptor
feign:
client:
config:
xxxx: # 遠(yuǎn)程服務(wù)名
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
requestInterceptors: com.leparts.config.FeignBasicAuthRequestInterceptor經(jīng)過測(cè)試,上面的解決方案可以正常的使用;但是出現(xiàn)了新的問題。
在轉(zhuǎn)發(fā)Feign的請(qǐng)求頭的時(shí)候, 如果開啟了Hystrix, Hystrix的默認(rèn)隔離策略是Thread(線程隔離策略), 因此轉(zhuǎn)發(fā)攔截器內(nèi)是無法獲取到請(qǐng)求的請(qǐng)求頭信息的。
可以修改默認(rèn)隔離策略為信號(hào)量模式:
hystrix.command.default.execution.isolation.strategy=SEMAPHORE
但信號(hào)量模式不是官方推薦的隔離策略;另一個(gè)解決方法就是自定義Hystrix的隔離策略。
自定義策略
HystrixConcurrencyStrategy 是提供給開發(fā)者去自定義hystrix內(nèi)部線程池及其隊(duì)列,還提供了包裝callable的方法,以及傳遞上下文變量的方法。所以可以繼承了HystrixConcurrencyStrategy,用來實(shí)現(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)用丟失請(qǐng)求頭的問題就解決的了 。
到此這篇關(guān)于SpringCloud微服務(wù)調(diào)用丟失請(qǐng)求頭的文章就介紹到這了,更多相關(guān)SpringCloud微服務(wù)調(diào)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于SpringMVC的數(shù)據(jù)綁定@InitBinder注解的使用
這篇文章主要介紹了關(guān)于SpringMVC的數(shù)據(jù)綁定@InitBinder注解的使用,在SpringMVC中,數(shù)據(jù)綁定的工作是由 DataBinder 類完成的,DataBinder可以將HTTP請(qǐng)求中的數(shù)據(jù)綁定到Java對(duì)象中,需要的朋友可以參考下2023-07-07
Java dom4j創(chuàng)建解析xml文檔過程解析
這篇文章主要介紹了Java dom4j創(chuàng)建解析xml文檔過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
Java集成大模型LangChain4j實(shí)戰(zhàn)完全指南(推薦)
LangChain4j 的目標(biāo)是簡(jiǎn)化將大語言模型(LLM - Large Language Model)集成到Java應(yīng)用程序中的過程,本文給大家介紹Java集成大模型LangChain4j實(shí)戰(zhàn)完全指南,感興趣的朋友跟隨小編一起看看吧2025-09-09
Spring整合多數(shù)據(jù)源實(shí)現(xiàn)動(dòng)態(tài)切換的實(shí)例講解
下面小編就為大家?guī)硪黄猄pring整合多數(shù)據(jù)源實(shí)現(xiàn)動(dòng)態(tài)切換的實(shí)例講解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07
使用SpringBoot注解方式處理事務(wù)回滾實(shí)現(xiàn)
這篇文章主要介紹了使用SpringBoot注解方式處理事務(wù)回滾實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
一小時(shí)迅速入門Mybatis之bind與多數(shù)據(jù)源支持 Java API
這篇文章主要介紹了一小時(shí)迅速入門Mybatis之bind與多數(shù)據(jù)源支持 Java API,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
Mybatis foreach用法解析--對(duì)于list和array
這篇文章主要介紹了Mybatis foreach用法解析--對(duì)于list和array,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
base64_encode和base64_decode的JAVA實(shí)現(xiàn)
Base64 編碼其實(shí)是將3個(gè)8位字節(jié)轉(zhuǎn)換為4個(gè)6位這4個(gè)六位字節(jié) 其實(shí)仍然是8位,只不過高兩位被設(shè)置為0. 當(dāng)一個(gè)字節(jié)只有6位有效時(shí),它的取值空間為0 到 2的6次方減1 即63,也就是說被轉(zhuǎn)換的Base64編碼的每一個(gè)編碼的取值空間為(0~63).需要的朋友可以參考下2016-04-04

