Feign启用hystrix跨服务header传参解决方案

解决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;
    }
}

 

上一篇:Hystrix服务隔离


下一篇:Spring Cloud Netflix 04 Hystrix