SpringCloud-Hystrix传播ThreadLocal对象
问题
在开发时遇到
当Feign开启Hystrix支持时,
public class KeycloakRequestInterceptor implements RequestInterceptor { private static final String AUTHORIZATION_HEADER = "Authorization"; @Override public void apply(RequestTemplate template) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); Principal principal = attributes.getRequest().getUserPrincipal(); if (principal != null && principal instanceof KeycloakPrincipal) { KeycloakSecurityContext keycloakSecurityContext = ((KeycloakPrincipal) principal) .getKeycloakSecurityContext(); if (keycloakSecurityContext instanceof RefreshableKeycloakSecurityContext) { RefreshableKeycloakSecurityContext.class.cast(keycloakSecurityContext) .refreshExpiredToken(true); template.header(AUTHORIZATION_HEADER, "Bearer " + keycloakSecurityContext.getTokenString()); } } // 否则啥都不干 } }你可能不知道Keycloak是什么,不过没有关系,相信这段代码并不难阅读,该拦截器做了几件事:
- 使用
RequestContextHolder.getRequestAttributes()静态方法获得Request。- 从Request获得当前用户的身份,然后使用Keycloak的API拿到Token,并扔到Header里。
- 这样,Feign使用这个拦截器时,就会用你这个Header去请求了。
现实很骨感
以上代码可完美运行——但仅限于Feign不开启Hystrix支持时。
注:Spring Cloud Dalston以及更高版可使用
feign.hystrix.enabled=true为Feign开启Hystrix支持。当Feign开启Hystrix支持时,
hystrix.command.default.execution.isolation.strategy: SEMAPHORE这样配置后,Feign可以正常工作。
但该方案不是特别好。原因是Hystrix官方强烈建议使用THREAD作为隔离策略! 参考文档:
Thread or Semaphore
The default, and the recommended setting, is to run
HystrixCommands using thread isolation (THREAD) andHystrixObservableCommands using semaphore isolation (SEMAPHORE).Commands executed in threads have an extra layer of protection against latencies beyond what network timeouts can offer.
Generally the only time you should use semaphore isolation for
HystrixCommands is when the call is so high volume (hundreds per second, per instance) that the overhead of separate threads is too high; this typically only applies to non-network calls.于是,那么有没有更好的方案呢?
解决方案二:自定义并发策略
既然Hystrix不太建议使用SEMAPHORE作为隔离策略,那么是否有其他方案呢?答案是自定义并发策略,目前,Spring Cloud Sleuth以及Spring Security都通过该方式传递
ThreadLocal对象。下面我们来编写自定义的并发策略。
编写自定义并发策略
编写自定义并发策略比较简单,只需编写一个类,让其继承
HystrixConcurrencyStrategy,并重写wrapCallable方法即可。代码示例:
public class RequestAttributeHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy { private static final Log log = LogFactory.getLog(RequestAttributeHystrixConcurrencyStrategy.class); private HystrixConcurrencyStrategy delegate; public RequestAttributeHystrixConcurrencyStrategy() { try { this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy(); if (this.delegate instanceof RequestAttributeHystrixConcurrencyStrategy) { // 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(); this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy); HystrixPlugins.reset(); HystrixPlugins.getInstance().registerConcurrencyStrategy(this); 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); } } private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier, HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) { if (log.isDebugEnabled()) { log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy [" + this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher [" + metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]"); log.debug("Registering Sleuth Hystrix Concurrency Strategy."); } } @Override public <T>