SpringBoot项目中使用到了feign作为Cloud平台内部微服务之间的接口调用,内部组件之间调用过程中,需要在RequestTemplate中添加请求头header鉴权等信息,而feign在开启了httpclient和hystrix支持后,发现RequestContextHolder.currentRequestAttributes()抛No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.异常。
yml引入feign、hystrix配置如下:
feign:
httpclient:
enabled: true
hystrix:
enabled: true
compression: #开启GZIP
request:
enabled: true
response:
enabled: true
hystrix:
threadpool:
default:
coreSize: 20 # 设置线程池大小为20
metrics:
enabled: true
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000
timeout:
enabled: false
hystrix使用多线程管理请求连接池,从主线程到发送基于hystrix的feign请求线程已不在同一个线程内,而RequestContextHolder是基于ThreadLocal实现的,这就使得线程之间数据断链,需要通过线程之间的数据传递使得ThreadLocal中存储的currentRequestAttributes接上。hystrix强大在于是支持此扩展操作的。
废话不多说,直接上代码:
package com.xx.xx.app.config;
import java.util.concurrent.Callable;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
/**
* @author zhouyuhhu
* @date 2019/12/26
*/
public class RequestContextHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new RequestAttributeAwareCallable<>(callable, RequestContextHolder.getRequestAttributes());
}
static class RequestAttributeAwareCallable<T> implements Callable<T> {
private final Callable<T> delegate;
private final RequestAttributes requestAttributes;
public RequestAttributeAwareCallable(Callable<T> callable, RequestAttributes requestAttributes) {
this.delegate = callable;
this.requestAttributes = requestAttributes;
}
@Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return delegate.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}
自定义策略方案实现后,现在的问题是如何让自己的策略注册上去生效,网上给了个方案如下:
@PostConstruct
public void init() {
HystrixPlugins.getInstance().registerConcurrencyStrategy(new RequestContextHystrixConcurrencyStrategy());
}
但是,报错:
Caused by: java.lang.IllegalStateException: Another strategy was already registered.
如果使用HystrixPlugins.reset();
太粗暴,把所有的HystrixPlugins都reset了个遍,其实这里仅仅是重定义了HystrixConcurrencyStrategy。
在工程的classpath下引入hystrix-plugins.properties配置文件,hystrix会默认读取hystrix-plugins.properties配置文件并生效自定义的plugins:
hystrix.plugin.HystrixConcurrencyStrategy.implementation=com.xx.xx.xx.config.RequestContextHystrixConcurrencyStrategy
最后,就可以在RequestTemplate中通过RequestContextHolder.currentRequestAttributes()添加header信息了,代码如下:
package com.xx.xx.xx.xx;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
/**
* @author zhouyuhhu
* @date 2019/9/19 17:17
*/
@Component
@Slf4j
public class FeignAccessTokenRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
// 请求MediaType
requestTemplate.header("Content-Type", MediaType.APPLICATION_JSON_UTF8.toString());
requestTemplate.header("Accept", MediaType.APPLICATION_JSON_UTF8.toString());
try {
HttpServletRequest request =
((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
if (null != request) {
//
requestTemplate.header("sysRestApiPermission", request.getHeader("sysRestApiPermission"));
// 渠道来源
requestTemplate.header("REQUEST-SOURCE", request.getHeader("REQUEST-SOURCE"));
// X-Auth-Token
requestTemplate.header("X-Auth-Token", request.getHeader("X-Auth-Token"));
// Authorization
requestTemplate.header("Authorization", request.getHeader("Authorization"));
}
} catch (Exception e) {
log.warn("getRequest fail!", e);
}
}
}