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)文章
詳解JDK自帶javap命令反編譯class文件和Jad反編譯class文件(推薦使用jad)
這篇文章主要介紹了JDK自帶javap命令反編譯class文件和Jad反編譯class文件(推薦使用jad),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09IntelliJ IDEA快速創(chuàng)建getter和setter方法
這篇文章主要介紹了IntelliJ IDEA快速創(chuàng)建getter和setter方法,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03classloader類加載器_基于java類的加載方式詳解
下面小編就為大家?guī)硪黄猚lassloader類加載器_基于java類的加載方式詳解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10springboot 基于Tomcat容器的自啟動(dòng)流程分析
這篇文章主要介紹了springboot 基于Tomcat容器的自啟動(dòng)流程分析,Spring通過注解導(dǎo)入Bean大體可分為四種方式,我們主要來說Import的兩種實(shí)現(xiàn)方法,需要的朋友可以參考下2020-02-02Spring Cloud Gateway調(diào)用Feign異步問題記錄
這篇文章主要介紹了Spring Cloud Gateway調(diào)用Feign異步問題記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04Java單元測(cè)試Powermockito和Mockito使用總結(jié)
公司單元測(cè)試框架選用了Junit 4.12,Mock框架選用了Mockito和PowerMock,本文主要介紹了Java單元測(cè)試Powermockito和Mockito使用總結(jié),感興趣的可以了解一下2021-09-09