在使用SpringCloud来构建微服务时,服务和服务之间的调用非常频繁,服务之间调用通常用feign和Hystrix结合来使用,当使用上游微服务调用下游微服务时,怎么将上游服务的请求信息传递到下游服务中去呢?Feign提供了Interceptor来设置请求下游服务的header等信息,如下:

@Component
public class FeignRequestInterceptor implements RequestInterceptor {@Value("${spring.application.name}")private String serviceName;@Overridepublic void apply(RequestTemplate requestTemplate) {requestTemplate.header("from-service", serviceName);}
}

  这样可以设置系统配置的数据到http请求header中,同样可以设置数据库的数据或者是其他服务提供的数据。但是要设置请求上游服务的请求header的话,就比较麻烦了,有人提供了以下解决方案

@Component
public class FeignRequestInterceptor implements RequestInterceptor {@Value("${spring.application.name}")private String serviceName;@Overridepublic void apply(RequestTemplate requestTemplate) {requestTemplate.header("from-service", serviceName);ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if(null != attributes){HttpServletRequest request = attributes.getRequest();String sessionId = request.getHeader("SESSIONID");if(!StringUtils.isEmpty(sessionId)){requestTemplate.header("SESSIONID", sessionId);}}}
}

这个方案在只使用feign的情况下没有问题,但是在feign和hystrix结合使用时,却有了问题,调试发现RequestContextHolder.getRequestAttributes()每次返回都是空,这是为什么呢?

查看RequestContextHolder的源码发现,RequestContextHolder将ServletRequestAttribute存放在了线程ThreadLocal中,而Hystrix默认的是多线程的,不是同一个线程,ThreadLocal中的数据自然拿不到,这个时候怎么解决这个问题呢?

1,调整hystrix执行的隔离策略。

给hystrix增加配置

hystrix: command:default: execution: isolation: strategy: SEMAPHORE

是什么意思呢?

strategy可以取THREAD和SEMAPHORE两种策略,两种策略分别是:

  • THREAD,HystrixCommand.run()在一个单独的线程中执行,即下游依赖的请求在一个单独的线程中隔离,并发请求数收到线程中线程数的限制。
  • SEMAPHORE,HystrixCommand.run()在调用线程中执行,即下游依赖请求在当前调用线程中执行,并发请求受信号量计数的限制

这样,上述配置就是告诉Hystrix使用SEMAPHORE隔离策略,调用和请求下游依赖在一个线程中执行,就可以访问同一个Threadlocal中的数据了。

但是,官方建议,执行HystrixCommand使用THREAD策略,执行HystrixObservableCommand使用SEMAPHORE策略。因为在线程中执行的命令对除网络超时以外的延迟有额外的保护层。通常,HystrixCommands使用信号量模式唯一的场景是,调用的并发量非常高(每个实例每秒数百个)是,这样线程的开销就非常高,通常用于线下的调用。

这样配置以后,RequestContextHolder.getRequestAttributes()就可以拿到数据了,但是hystrix官方却不推荐这么做,所以暂不考虑。

官方文档见:https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.strategy

所以这种方式可以实现,但是不推荐。

2,重写THREAD策略,将ServletRequestAttribute传入到hystrix熔断执行下游请求的线程中。

首先,查看查看Hystrix的THREAD策略的实现,见(HystrixConcurrencyStrategy),发现wrapCallable方法提供了一个在请求执行前覆写的机会,如下

/*** Provides an opportunity to wrap/decorate a {@code Callable<T>} before execution.* <p>* This can be used to inject additional behavior such as copying of thread state (such as {@link ThreadLocal}).* <p>* <b>Default Implementation</b>* <p>* Pass-thru that does no wrapping.* * @param callable*            {@code Callable<T>} to be executed via a {@link ThreadPoolExecutor}* @return {@code Callable<T>} either as a pass-thru or wrapping the one given*/
public <T> Callable<T> wrapCallable(Callable<T> callable) {return callable;
}

重写wrapCallable方法,添加自定义逻辑

@Component
public class HystrixConcurrencyStrategyCustomize extends HystrixConcurrencyStrategy {public <T> Callable<T> wrapCallable(Callable<T> callable) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HystrixCustomizeCallable hystrixCustomizeCallable = new HystrixCustomizeCallable(attributes, callable);return hystrixCustomizeCallable;}}class HystrixCustomizeCallable<T> implements Callable<T>{private ServletRequestAttributes attributes;private Callable<T> callable;public HystrixCustomizeCallable(ServletRequestAttributes attributes, Callable<T> callable){this.attributes = attributes;this.callable = callable;}@Overridepublic T call() throws Exception {try{if(null != this.attributes){RequestContextHolder.setRequestAttributes(this.attributes);}return this.callable.call();}finally {RequestContextHolder.resetRequestAttributes();}}
}

使策略生效

自定义的策略如何在编写后生效呢?Hystrix官网中提供了解决方案

If you wish to register a plugin before you invoke the HystrixCommand for the first time, you may do so with code like the following:

HystrixPlugins.getInstance().registerEventNotifier(ACustomHystrixEventNotifierDefaultStrategy.getInstance());

这样,在自定义的并发策略的基础上,再在Feign的RequestInterceptor中为请求添加header,将上游请求的session信息传递到下游微服务。

但是这样的解决方案在应用加入actuator是依然后问题。

Caused by: java.lang.IllegalStateException: Another strategy was already registered.

提示已经注册策略,不能重复注册,参考下Sleuth以及Spring Security的实现,先删除已经注册的策略,重新注册新的策略,如下:

@Configuration
public class HystrixConfig {public static final Logger log = LoggerFactory.getLogger(HystrixConfig.class);public HystrixConfig(){try {HystrixConcurrencyStrategy target = new HystrixConcurrencyStrategyCustomize();HystrixConcurrencyStrategy strategy = HystrixPlugins.getInstance().getConcurrencyStrategy();if (strategy instanceof HystrixConcurrencyStrategyCustomize) {// 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();if (log.isDebugEnabled()) {log.debug("Current Hystrix plugins configuration is ["+ "concurrencyStrategy [" + target + "]," + "eventNotifier ["+ eventNotifier + "]," + "metricPublisher [" + metricsPublisher + "],"+ "propertiesStrategy [" + propertiesStrategy + "]," + "]");log.debug("Registering Sleuth Hystrix Concurrency Strategy.");}HystrixPlugins.reset();HystrixPlugins.getInstance().registerConcurrencyStrategy(target);HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);}catch (Exception e) {log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);}}
}

(完)

Feign Hystrix微服务调用Session传播相关推荐

  1. [Spring cloud 一步步实现广告系统] 11. 使用Feign实现微服务调用

    上一节我们使用了Ribbon(基于Http/Tcp)进行微服务的调用,Ribbon的调用比较简单,通过Ribbon组件对请求的服务进行拦截,通过Eureka Server 获取到服务实例的IP:Por ...

  2. feign响应拦截_[Spring cloud 一步步实现广告系统] 11. 使用Feign实现微服务调用

    上一节我们使用了Ribbon(基于Http/Tcp)进行微服务的调用,Ribbon的调用比较简单,通过Ribbon组件对请求的服务进行拦截,通过Eureka Server 获取到服务实例的IP:Por ...

  3. 微服务feignclient_[Spring cloud 一步步实现广告系统] 11. 使用Feign实现微服务调用

    上一节我们使用了Ribbon(基于Http/Tcp)进行微服务的调用,Ribbon的调用比较简单,通过Ribbon组件对请求的服务进行拦截,通过Eureka Server 获取到服务实例的IP:Por ...

  4. 微服务调用组件Feign实战

    文章目录 一.JAVA 项目中如何实现远程接口调用? 二.什么是Feign 2.2 Feign的优势 2.2 Feign的设计架构与底层原理源码 2.3 Ribbon&Feign对比 Ribb ...

  5. spring cloud 微服务调用--ribbon和feign调用

    这里介绍ribbon和feign调用两种通信服务调用方式,同时介绍如何引入第三方服务调用.案例包括了ribbon负载均衡和hystrix熔断--服务降级的处理,以及feign声明式服务调用.例子包括s ...

  6. 【Spring Cloud Alibaba】(二)微服务调用组件Feign原理+实战

    系列目录 [Spring Cloud Alibaba](一)微服务介绍 及 Nacos注册中心实战 本文目录 系列目录 前言 什么是RPC? Feign和OpenFeign都是什么? HTTP调用 v ...

  7. SpringCloud Feign声明式服务调用

    SpringCloud Feign声明式服务调用 1. 加入pom依赖 2. Application.java上声明@EnableFeignClients 3. @FeignClient声明接口调用服 ...

  8. dubbo consumer 端口_基于Springboot+Dubbo+Nacos 注解方式实现微服务调用

    今天跟大家分享基于Springboot+Dubbo+Nacos 注解方式实现微服务调用的知识. 1 项目结构 |-- spring-boot-dubbo-demo (父级工程) |-- spring- ...

  9. JSD-2204-Dubbo实现微服务调用-Seata-Day04

    1.Dubbo实现微服务调用 1.1确定调用关系 order模块调用stock模块的减少库存的功能 order模块调用cart模块的删除购物车的功能 business模块调用order新增订单的功能 ...

最新文章

  1. HBase应用快速学习
  2. OpenCV辅助对象(help objects)(3)——Ptr
  3. 全局路径规划:图搜索算法介绍1(BFS/DFS)
  4. (转!)Netdata---Linux系统性能实时监控平台部署
  5. android简单小项目实例_自学(系统学)Python了那么久, 想就业? 几个简单小项目让你通过面试!...
  6. 如何使用 Mac 的通知中心?
  7. Linux rhel7 下MySQL5.7.18详细安装文档
  8. android 函数式编程,响应式编程在Android中的应用
  9. C语言开发环境搭建过程
  10. B2C电商项目(第八天、用户认证、单点登录、Oauth2认证、项目认证开发、认证服务对接网关、登录页、SpringSecurity 权限控制)
  11. 干货|爬虫被封的几个常见原因
  12. win10蓝牙android上网,Win10开启蓝牙移动热点共享上网教程
  13. web端的兼容性测试
  14. arm指令bne.w改成b,即无条件跳转
  15. Vant Tab标签页
  16. 【论文解读】R-CNN 深入浅出理解目标检测开山之作
  17. 【Unity面试】 C#语言基础核心 | 面试真题 | 全面总结 | 建议收藏
  18. Ubuntu 16.04下NVIDIA GTX 960M显卡驱动的安装
  19. D35 Spark源代码(待补充)
  20. webpack转化es6语法

热门文章

  1. ie6,ie7兼容性总结(转)
  2. 用dos复制文件_一文带你熟悉DOS命令操作,CMD从此不再是路人!
  3. python数据库实现注册函数_python 函数 之 用户注册register()
  4. ipad远程连接虚拟机linux,如何从ipad pro上通过SSH远程Linux
  5. linux内核内存映射实验报告,动手实践-Linux内存映射基础(上)
  6. as cast float server sql_SQL语言在数据工程(Data Engineering)中的运用(一)
  7. mysql创建表时报150_Mysql创建表时报错error150
  8. git 提交代码命令_提交代码:git push 命令的四种形式
  9. 过年,设计师都爱的烫金红色PSD装饰素材
  10. 关于网站主页的界面设计不同风格的探索