Feign调用丢失Header的解决方案
文章首发于公众号《程序员果果》
地址 : https://mp.weixin.qq.com/s/a99TeOxgZGNgFkpT6gmA3g目前累计服务客户上千,积累了丰富的产品开发及服务经验。以网站设计水平和技术实力,树立企业形象,为客户提供网站建设、做网站、网站策划、网页设计、网络营销、VI设计、网站改版、漏洞修补等服务。成都创新互联公司始终以务实、诚信为根本,不断创新和提高建站品质,通过对领先技术的掌握、对创意设计的研究、对客户形象的视觉传递、对应用系统的结合,为客户提供更好的一站式互联网解决方案,携手广大客户,共同发展进步。
问题
在 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 headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
requestTemplate.header(name, values);
}
}
Enumeration 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 Callable wrapCallable(Callable callable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new WrappedCallable<>(callable, requestAttributes);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty corePoolSize,
HystrixProperty maximumPoolSize,
HystrixProperty keepAliveTime,
TimeUnit unit, BlockingQueue 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 getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
@Override
public HystrixRequestVariable getRequestVariable(HystrixRequestVariableLifecycle rv) {
return this.delegate.getRequestVariable(rv);
}
static class WrappedCallable implements Callable {
private final Callable target;
private final RequestAttributes requestAttributes;
WrappedCallable(Callable 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调用丢失请求头的问题就解决的了 。
参考
https://blog.csdn.net/zl1zl2zl3/article/details/79084368
https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.0.RC2/reference/html/
关注我
当前文章:Feign调用丢失Header的解决方案
当前路径:http://azwzsj.com/article/ppcgsd.html