ThreadLocal Context Propagation with Hystrix

The article explains how to propagate ThreadLocal data such as a traceId across Hystrix’s thread‑pool isolation by implementing a custom HystrixConcurrencyStrategy or, more robustly, a HystrixCommandExecutionHook that uses HystrixRequestContext, ensuring the context is correctly transferred even to fallback methods.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
ThreadLocal Context Propagation with Hystrix

This article shares a solution for passing ThreadLocal context information when using Hystrix.

Background : In business development, ThreadLocal is used to store key information such as traceId. When Hystrix is applied for circuit breaking, the ThreadLocal value may not be correctly propagated to the Hystrix thread.

ThreadLocal : Explanation and example code for storing traceId.

public class ThreadLocalUtil {
    private static final ThreadLocal<String> TRACE_ID = new ThreadLocal<>();
    public static void setTraceId(String traceId) { TRACE_ID.set(traceId); }
    public static String getTraceId() { return TRACE_ID.get(); }
    public static void clearTraceId() { TRACE_ID.remove(); }
}

Hystrix : Overview of Hystrix circuit breaker and its two isolation strategies (semaphore and thread‑pool). The default is thread‑pool, which creates a separate thread pool for command execution.

Problem : In thread‑pool mode, ThreadLocal values are not automatically transferred to the Hystrix thread, leading to mismatched traceId.

Solution 1 – InheritableThreadLocal : Not suitable because Hystrix reuses threads from its pool, causing stale values.

Solution 2 – HystrixConcurrencyStrategy : Implement a custom concurrency strategy that copies the ThreadLocal value before the command runs and clears it afterwards.

public class MyHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        String traceId = ThreadLocalUtil.getTraceId();
        return () -> {
            ThreadLocalUtil.setTraceId(traceId);
            try {
                return callable.call();
            } finally {
                ThreadLocalUtil.clearTraceId();
            }
        };
    }
}
// Register
HystrixPlugins.getInstance().registerConcurrencyStrategy(new MyHystrixConcurrencyStrategy());

This approach works for normal command execution but does not cover fallback methods.

Solution 3 – HystrixCommandExecutionHook : Override hook methods to copy and paste the traceId for onStart, onExecutionStart, onFallbackStart, and clean up after success or error.

public class MyHystrixHook extends HystrixCommandExecutionHook {
    private String traceId;
    @Override
    public <T> void onStart(HystrixInvokable<T> commandInstance) { copyTraceId(); }
    @Override
    public <T> void onExecutionStart(HystrixInvokable<T> commandInstance) { pasteTraceId(); }
    @Override
    public <T> void onFallbackStart(HystrixInvokable<T> commandInstance) { pasteTraceId(); }
    // option1
    @Override
    public <T> void onExecutionSuccess(HystrixInvokable<T> commandInstance) {
        ThreadLocalUtil.clearTraceId();
        super.onExecutionSuccess(commandInstance);
    }
    @Override
    public <T> Exception onExecutionError(HystrixInvokable<T> commandInstance, Exception e) {
        ThreadLocalUtil.clearTraceId();
        return super.onExecutionError(commandInstance, e);
    }
    // option2 …
    private void copyTraceId() { this.traceId = ThreadLocalUtil.getTraceId(); }
    private void pasteTraceId() { ThreadLocalUtil.setTraceId(traceId); }
}
// Register
HystrixPlugins.getInstance().registerCommandExecutionHook(new MyHystrixHook());

Potential race condition: multiple caller threads may overwrite the shared traceId in the hook. To avoid this, Hystrix provides HystrixRequestContext and HystrixRequestVariableDefault.

HystrixRequestContext holds a ThreadLocal of request‑scoped data; HystrixRequestVariableDefault works like a ThreadLocal with get/set backed by the context’s ConcurrentHashMap.

public class MyHystrixHook extends HystrixCommandExecutionHook {
    private HystrixRequestVariableDefault<String> requestVariable = new HystrixRequestVariableDefault<>();
    @Override
    public <T> void onStart(HystrixInvokable<T> commandInstance) {
        HystrixRequestContext.initializeContext();
        copyTraceId();
    }
    @Override
    public <T> void onExecutionStart(HystrixInvokable<T> commandInstance) { pasteTraceId(); }
    @Override
    public <T> void onFallbackStart(HystrixInvokable<T> commandInstance) { pasteTraceId(); }
    @Override
    public <T> void onSuccess(HystrixInvokable<T> commandInstance) {
        HystrixRequestContext.getContextForCurrentThread().shutdown();
        super.onSuccess(commandInstance);
    }
    @Override
    public <T> Exception onError(HystrixInvokable<T> commandInstance,
                                 HystrixRuntimeException.FailureType failureType, Exception e) {
        HystrixRequestContext.getContextForCurrentThread().shutdown();
        return super.onError(commandInstance, failureType, e);
    }
    private void copyTraceId() { requestVariable.set(ThreadLocalUtil.getTraceId()); }
    private void pasteTraceId() { ThreadLocalUtil.setTraceId(requestVariable.get()); }
}
// Register
HystrixPlugins.getInstance().registerCommandExecutionHook(new MyHystrixHook());

Hystrix’s internal wrapper copies the caller’s HystrixRequestContext to the execution thread, ensuring the traceId is correctly transferred.

Summary : Choose between HystrixConcurrencyStrategy (simpler, works when fallback does not need context) and HystrixCommandExecutionHook with HystrixRequestContext (more robust, covers fallback). Both plugins must be registered before use.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaThreadLocalHystrixContextPropagation
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.