feign 调用服务请求头丢失
feign 调用服务请求头丢失
服务 A 通过 Feign 调用服务 B ,服务 B 无法获取服务 A 的 header
排查过程
- 先在 feign 调用的方法上打断点,走到 SynchronousMethodHandler
- 进到 invoke 方法后发现有一个 create 方法,而创建的 RequestTemplate 的 header 是空的
- 到 executeAndDecode 方法后发现有一个 targetRequest 的方法,不知道做什么用的,而获取响应结果的方法的 Request 的请求头也是空的
- request 的 header 是空的,所以服务 B 获取不到值
解决方案
- 上面发现有一个不知道做什么的方法,传进去一个 RquestTemplate 返回一个 Request,那么点进去
- 我们不知道这个 interceptors 是哪来的,但是初始化默认为空集合,以多年使用 spring 的经验来看,猜测是对 template 进行增强的,那么我们重写该方法。(想要知道从哪里集成的 requestInterceptor 看 FeignClientFactoryBean.java:146,这里不进行讲解)
@Component
public class FeignExecBeforeInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
HttpServletRequest request = requestAttributes.getRequest();
// 拿到原始请求头数据
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
String headerValue = request.getHeader(header);
if (StringUtils.isNotEmpty(headerValue)) {
template.header(header, headerValue);
}
}
}
}
}
feign 开启 hystrix 请求头丢失
feign 开启 hystrix 后 RequestContextHolder.getRequestAttributes() 为 null
排查过程
- 断点发现,执行的类由 SynchronousMethodHandler 变为 HystrixInvocationHandler,说明 feign 集成了 hystrix
解决方案 1:调整隔离策略
将隔离策略调整为 semaphore 即可(配置对应的类:com.netflix.hystrix.HystrixCommandProperties#HystrixCommandProperties(com.netflix.hystrix.HystrixCommandKey, com.netflix.hystrix.HystrixCommandProperties.Setter, java.lang.String))
hystrix.command.default.execution.isolation.strategy: SEMAPHORE
但是官网建议:
一般来说,你应该使用信号量隔离(SEMAPHORE)的唯一场景是调用量太大(每个实例每秒数百次),以至于单独线程的开销太高;这通常仅适用于非网络调用。
解决方案 2 :自定义并发策略
自定义并发策略,将请求头设置到 threadLocal 里
@Slf4j
@Component
public class RequestAttributeHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
public RequestAttributeHystrixConcurrencyStrategy() {
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new WrappedCallable<>(callable, requestAttributes);
}
static class WrappedCallable<T> implements Callable<T> {
private final Callable<T> target;
private final RequestAttributes requestAttributes;
public 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();
}
}
}
}
Hystrix 只允许有一个并发策略!
最终解决方案
转载自:https://juejin.cn/post/7210691957789081657