解决header中参数丢失问题目前我所知道的方案有两种,方案一最简单,适用于单一方法需要通过header传参,只需要在方法加个参数,在控制层的方法获取header中的参数后作为参数传递即可。方案二适用全局参数比如token加密密钥等,这种情况就需要统一处理。
介绍解决方案之前先对hystrix的插件功能进行一些简单介绍。
Hystrix提供插件功能,我们通过实现hystrix提供的插件接口可以实现自定义行为。hystrix提供以下几种类型的插件Event Notifier、Metrics Publisher、Properties Strategy、Concurrency Strategy、Command Execution Hook。各种查询具体功能可以参考官方wiki说明,https://github.com/Netflix/Hystrix/wiki/Plugins#plugins
下面以Command Execution Hook为例怎么注册一个自定义的插件。
Command Execution Hook
Command Execution Hook 顾名思义就是一个钩子函数,它可以让你访问到hystrix执行的生命周期中(HystrixCommand
or HystrixObservableCommand
),因此你注入你想定制行为,比如记录日志,重写返回结果,修改线程状态等。你可以重写具体的钩子函数,它提供的钩子函数有以下:
HystrixCommandExecutionHook method |
when Hystrix calls the method |
---|---|
onStart |
before the HystrixInvokable begins executing |
onEmit |
whenever the HystrixInvokable emits a value |
onError |
if the HystrixInvokable fails with an exception |
onSuccess |
if the HystrixInvokable completes successfully |
onThreadStart |
at the start of thread execution if the HystrixInvokable is a HystrixCommand executed using the THREAD ExecutionIsolationStrategy
|
onThreadComplete |
at the completion of thread execution if the HystrixInvokable is a HystrixCommand executed using the THREAD ExecutionIsolationStrategy
|
onExecutionStart |
when the user-defined execution method in the HystrixInvokable begins |
onExecutionEmit |
whenever the user-defined execution method in the HystrixInvokable emits a value |
onExecutionError |
when the user-defined execution method in the HystrixInvokable fails with an exception |
onExecutionSuccess |
when the user-defined execution method in the HystrixInvokable completes successfully |
onFallbackStart |
if the HystrixInvokable attempts to call the fallback method |
onFallbackEmit |
whenever the fallback method in the HystrixInvokable emits a value |
onFallbackError |
if the fallback method in the HystrixInvokable fails with an exception or does not exist when a call attempt is made |
onFallbackSuccess |
if the fallback method in the HystrixInvokable completes successfully |
onCacheHit |
if the response to the HystrixInvokable is found in the HystrixRequestCache
|
onStart就是hystrix执行方法调用之前执行。
如果你想着hystrix执行方法之前对参数进行处理或者做其他事情就可以重写这个方法。
下面我已一个使用场景举例说明,在微服务架构下我们经常使用feign进行服务调用,有些请求参数我们是通过header传递的,当需要跨服务传递的使用,我们通过实现RequestInterceptor把参数重新包装到RequestTemplate的header,这样我们在另外的服务就可以获取到。如果不重新包装是获取不到,因为feign发起调用是重新构建的请求对象,并没有包含原始的request对应请求头信息。拦截器实现如下:
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
@Component
@Slf4j
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null){
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);
}
}
}
}
}
这样当服务A调用服务B的时候,服务B就还可以获取请求头里面的参数信息。
但是当我们启用feign熔断开关后,如果我们使用的线程隔离策略,上述代码获取的ServletRequestAttributes就会为空。通过查看RequestContextHolder源码可以知道,这是因为RequestContextHolder底层是通过ThreadLocal保存ServletRequestAttributes对象的,而且默认是不共享的,所以子线程就获取不到父线程的ServletRequestAttributes的。所以我们可以父线程调用子线程之前重新把ServletRequestAttributes赋值,可以在子线程中就可以获取到ServletRequestAttributes对象了。
下面hystrix自定义钩子实现代码示例
import com.netflix.hystrix.HystrixInvokable;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
@Slf4j
public class HystrixCommandHook extends HystrixCommandExecutionHook {
@Override
public <T> void onStart(HystrixInvokable<T> commandInstance) {
log.info("onStart 开始执行-----");
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
RequestContextHolder.setRequestAttributes(requestAttributes,true);
log.info("onStart end -----");
super.onStart(commandInstance);
}
}
注册自定义钩子到hystrix中
import com.netflix.hystrix.strategy.HystrixPlugins;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HystrixConfiguration {
@Bean
public HystrixCommandHook HystrixCommandHook(){
HystrixCommandHook hystrixCommandHook = new HystrixCommandHook();
HystrixPlugins.getInstance().registerCommandExecutionHook(hystrixCommandHook);
return hystrixCommandHook;
}
}