Skip to content

添加@HystrixCommand导致ThreadLocal失效或null #6

Description

@dengly

在项目里使用了@HystrixCommand会导致ThreadLocal null。而我们使用的是THREAD隔离策略。网上搜索也知道了原因,可以参考这篇博文

但是最终他的处理办法不推荐,在调试和看spring-security-coreorg.springframework.cloud.netflix.hystrix.security.SecurityContextConcurrencyStrategyorg.springframework.security.concurrent.DelegatingSecurityContextCallable给了我们解决办法。

添加以下类即可解决问题

@Configuration
public class ApiHystrixConfig {

    @Autowired(required = false)
    private HystrixConcurrencyStrategy existingConcurrencyStrategy;


    @PostConstruct
    public void init() {
        // Keeps references of existing Hystrix plugins.   保留现有Hystrix插件。
        HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance()
                .getEventNotifier();
        HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance()
                .getMetricsPublisher();
        HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()
                .getPropertiesStrategy();
        HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance()
                .getCommandExecutionHook();

        HystrixPlugins.reset();

        // Registers existing plugins excepts the Concurrent Strategy plugin. 注册除Concurrent Strategy插件之外的现有插件。
        HystrixPlugins.getInstance().registerConcurrencyStrategy(
                new ApiContextConcurrencyStrategy(existingConcurrencyStrategy));
        HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
        HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
        HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
        HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
    }

    class ApiContextConcurrencyStrategy extends HystrixConcurrencyStrategy {
        private HystrixConcurrencyStrategy existingConcurrencyStrategy;

        public ApiContextConcurrencyStrategy(HystrixConcurrencyStrategy existingConcurrencyStrategy) {
            this.existingConcurrencyStrategy = existingConcurrencyStrategy;
        }

        @Override
        public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
            return existingConcurrencyStrategy != null
                    ? existingConcurrencyStrategy.getBlockingQueue(maxQueueSize)
                    : super.getBlockingQueue(maxQueueSize);
        }

        @Override
        public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
            return existingConcurrencyStrategy != null
                    ? existingConcurrencyStrategy.getRequestVariable(rv)
                    : super.getRequestVariable(rv);
        }

        @Override
        public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                                                HystrixProperty<Integer> corePoolSize,
                                                HystrixProperty<Integer> maximumPoolSize,
                                                HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
                                                BlockingQueue<Runnable> workQueue) {
            return existingConcurrencyStrategy != null
                    ? existingConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize,
                    maximumPoolSize, keepAliveTime, unit, workQueue)
                    : super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize,
                    keepAliveTime, unit, workQueue);
        }

        @Override
        public <T> Callable<T> wrapCallable(Callable<T> callable) {
            return existingConcurrencyStrategy != null
                    ? existingConcurrencyStrategy
                    .wrapCallable(new ApiContextCallable<T>(callable))
                    : super.wrapCallable(new ApiContextCallable<T>(callable));
        }
    }

    class ApiContextCallable<V> implements Callable<V> {
        private final Callable<V> delegate;
        private final Object delegateObject;
        private Object originalObject;

        public ApiContextCallable(Callable<V> delegate, Object delegateObject) {
            Assert.notNull(delegate, "delegate cannot be null");
            this.delegate = delegate;
            this.delegateObject = delegateObject;
        }

        public ApiContextCallable(Callable<V> delegate) {
            this(delegate, TokenContext.get());
        }

        public V call() throws Exception {
            try {
                // 重点在这里 TokenContext 是我自己的上下文
                // TokenContext.get() 和 TokenContext.set() 都是对自己的ThreadLocal操作
                this.originalObject = TokenContext.get();
                TokenContext.set(this.delegateObject);
                V reV = this.delegate.call();
                return reV;
            } finally {
                TokenContext.set(this.originalObject);
                this.originalObject = null;
            }

        }

        public String toString() {
            return this.delegate.toString();
        }
    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions