Feign 和 Hytrix 在微服務上下游的請求頭資訊傳遞丟失問題
在 Spring Cloud 中 微服務之間的呼叫會用到Feign,但是在預設情況下,Feign 呼叫遠端服務存在Header請求頭丟失問題。
解決方案
首先需要寫一個 Feign請求攔截器,通過實現RequestInterceptor介面,完成對所有的Feign請求,傳遞請求頭和請求引數。
Feign 請求攔截器
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);
requestTemplate.header(name, values);
}
}
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
經過測試,上面的解決方案可以正常的使用;但是出現了新的問題。
在轉發Feign的請求頭的時候, 如果開啟了Hystrix, Hystrix的預設隔離策略是Thread(執行緒隔離策略), 因此轉發攔截器內是無法獲取到請求的請求頭資訊的。
可以修改預設隔離策略為訊號量模式:
hystrix.command.default.execution.isolation.strategy=SEMAPHORE
但訊號量模式不是官方推薦的隔離策略;另一個解決方法就是自定義Hystrix的隔離策略。
自定義策略
HystrixConcurrencyStrategy 是提供給開發者去自定義hystrix內部執行緒池及其佇列,還提供了包裝callable的方法,以及傳遞上下文變數的方法。所以可以繼承了HystrixConcurrencyStrategy,用來實現了自己的併發策略。
@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();
}
}
}
}
致此,Feign呼叫丟失請求頭的問題就解決的了 。
相關文章
- 如何檢測 Web 服務請求丟失問題Web
- Web-請求資料+號丟失問題Web
- Nginx轉發導致請求頭丟失Nginx
- SpringCloud解決feign呼叫token丟失問題SpringGCCloud
- 使用Feign傳送HTTP請求HTTP
- GET請求的引數丟失
- 請求引數的傳遞
- Vue 使用 Axios 傳送請求的請求體問題VueiOS
- WKWebView 網路請求Header 丟失WebViewHeader
- Flask中請求資料的優雅傳遞Flask
- feign之間傳遞oauth2-token的問題和解決OAuth
- RocketMq訊息丟失問題解決MQ
- 解決.NET Core Ajax請求後臺傳送引數過大請求失敗問題
- spring cloud微服務快速教程之(十四)spring cloud feign使用okhttp3--以及feign呼叫引數丟失的說明SpringCloud微服務HTTP
- gateway(二)微服務之間傳遞使用者資訊Gateway微服務
- 如何處理RabbitMQ 訊息堆積和訊息丟失問題MQ
- vue傳參頁面重新整理資料丟失問題Vue
- Go 微服務:基於 RabbitMQ 和 AMQP 進行訊息傳遞Go微服務MQ
- vue2.0 axios post請求傳參問題(ajax請求)VueiOS
- Feign 呼叫丟失Header的解決方案Header
- 在html中使用axios傳送請求到servlet時遇到的傳值問題HTMLiOSServlet
- 如何避免 HttpClient 丟失請求頭:透過 HttpRequestMessage 解決並最佳化HTTPclient
- 微服務通訊之feign的配置隔離微服務
- ajax中設定請求頭和自定義請求頭
- 關於在request請求時,處理請求引數的問題
- 微服務的全鏈路請求(RequestContextHolder)微服務Context
- Django資料庫連線丟失問題Django資料庫
- 資料庫高可靠,輕鬆解決事務丟失問題資料庫
- 記錄環信IM使用restful介面時遇到的傳送PUT請求失敗的問題REST
- vue請求後端資料和跨域問題Vue後端跨域
- 大請求、請求超時問題
- 實際業務處理 Kafka 訊息丟失、重複消費和順序消費的問題Kafka
- 微服務通訊之feign整合負載均衡微服務負載
- 微服務互相呼叫-Feign微服務
- 微服務呼叫元件 Feign微服務元件
- RabbitMQ如何解決被重複消費和資料丟失的問題?MQ
- 有趣的請求引數/請求頭
- URL請求不能解決中文請求的問題