-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is there a way to automatically pass thread static information to the Hystrix Command thread pool? #92
Comments
We do exactly this type of use case at Netflix and use the HystrixConcurrencyStrategy to accomplish it. The ConcurrencyStrategy and ExecutionHook plugins overlap somewhat in their ability to handle this use case depending on how you want to do it. The
For example, the internal Netflix plugin implementation does this: @Override
public <K> Callable<K> wrapCallable(Callable<K> c) {
return new ContextCallable<K>(c);
} The ContextCallable copies thread state from the calling thread onto the child thread and then cleans up as that thread finishes. More advanced behavior can be done by implementing @Override
public <K> HystrixRequestVariable<K> getRequestVariable(HystrixRequestVariableLifecycle<K> rv) {
return new NetflixRequestVariable<K>(rv);
} In this case Netflix overrides the default Hystrix functionality since we already have a "request variable" and prefer to use it so decorate the one we have with the Hystrix interface. Unless you already have this type of behavior don't bother with that and just stick to Your implementation of a wrapping callable would be similar to this: https://github.com/Netflix/Hystrix/blob/master/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixContextCallable.java The difference is that you'll copy whatever context you have: public class YourCallableWrapper<K> implements Callable<K> {
private final Callable<K> actual;
private final SomeContext parentThreadState;
public YourCallableWrapper(Callable<K> actual) {
this.actual = actual;
// copy whatever state such as MDC
this.parentThreadState = SomeContext.getContextForCurrentThread();
}
@Override
public K call() throws Exception {
SomeContext existingState = SomeContext.getContextForCurrentThread();
try {
// set the state of this thread to that of its parent
// this is where the MDC state and other ThreadLocal values can be set
SomeContext.setContextOnCurrentThread(parentThreadState);
// execute actual Callable with the state of the parent
return actual.call();
} finally {
// restore this thread back to its original state
SomeContext.setContextOnCurrentThread(existingState);
}
}
} Then you'd implement `wrapCallable' as: @Override
public <K> Callable<K> wrapCallable(Callable<K> c) {
return new YourCallableWrapper<K>(c);
} |
Hi @pparth, Did the information above help or do you still have further questions on how to do this? |
Hello, Thank you for all your interest! I used my version of a HystrixConcurrentStrategy where i override the wrapCallable method that provides my implementation of a Callable as in your example. I registered my strategy using: HystrixPlugins.getInstance().registerConcurrencyStrategy(new O2HystrixConcurrencyStrategy()); Callable: public class O2HystrixContextCallable implements Callable {
} Strategy: public class O2HystrixConcurrencyStrategy extends HystrixConcurrencyStrategy { Nevertheless, when i try to run it, i get the exception that says: HystrixRequestContext.initializeContext() must be called at the beginning of each request before RequestVariable functionality can be used. which is odd, as i didn't enable caching anywhere... |
Yeah ... that's due to me "cheating" when I know the default implementation is being used since most people won't implement a custom concurrency strategy (at least I don't think so ... but could be wrong). You can turn off the RequestLog as well to avoid this if you want. It was a discussion point about enabling/disabling that functionality by default: #53 I imagine you've already seen this for how to initialize the context: https://github.com/Netflix/Hystrix/wiki/How-To-Use#wiki-RequestContextSetup What are your thoughts on what should be the default behavior? |
I initialized the context in a servlet filter, which seems to be the perfect place to do so, and everything worked like a charm! Not so sure if most people will not have thread info propagation issues though. Unless we are talking about the simplest of applications (and what is the need to use Hystrix for these in the first place, probably when the real benefit of using it, is never clearly understood...), someone will eventually like to pass information from the request thread to the Hystrix pool. |
You're probably right ... so perhaps I should add more information around this subject. Do you mind if I use a slightly modified version of your code copying the MDC state across threads on the wiki? |
Of course! Go ahead! Happy to be of little help! |
Is it possible to configure what the default strategy should be in a properties file, instead of doing it at the code level? hystrix.defaultconcurrencystrategy.properties: That way, we could bundle the configuration in an internal/company-level commons library on top of Hystrix. |
Archaius is the library used to do configuration and it supports properties files: https://github.com/Netflix/archaius Hystrix itself delegates all configuration to Archaius and/or the plugins that can be implemented. -- On March 2, 2015 at 2:49:30 AM, Eirik Sletteberg (notifications@github.com) wrote: Is it possible to configure what the default strategy should be in a properties file, instead of doing it at the code level? hystrix.defaultconcurrencystrategy.properties: That way, we could bundle the configuration in an internal/company-level commons library on top of Hystrix. — |
Currently, Hystrix lets you define custom strategies in two ways: 1) with System properties 2) with bootstrapping via HystrixPlugins.getInstance().registerXXX If neither is specified, the default strategy is used. This change replaces hardwired System.getProperty calls with lookup via Archaius. So one can override the plugin strategies used with any configuration provider Archaius is configured with. Most importantly, it has the effect of checking any file called "config.properties" on the classpath as well as using system properties. This lets you configure plugin strategies without having to write Java code for it, or having to run the application with additional system properties. Fixes Netflix#92
Hello, I've just run into the same issues when implementing my custom HystrixConcurrencyStrategy – I need to wrap callables so that they'd transfer a ThreadLocal variable. The customization is not related to request context and, since what I'm preparing is a library, I'd rather not control the request log options nor register filters for initializing request context for my clients. Perhaps we can think of an idea to make customizations to HystrixConcurrencyStrategy possible when they are not related to the use of request context. Why do we support using request context optionally when the default strategy is used and enforce context initialization for custom strategies? |
The key reason it is optional in the default mode is to make it easy to get started with Hystrix when none of the concurrency strategy or request context behaviors are being used. As soon as any of those features get touched then the lifecycle is required. This decision was made primarily out of simplicity, and because Hystrix can not know if a HystrixConcurrencyStrategy needs a RequestContext lifecycle or not, so defaults to requiring it to ensure functional correctness. If you can propose changes that are safe, understandable, deterministic, and backward compatible for allowing a HystrixConcurrencyStrategy to be implemented without needing a RequestContext lifecycle, then we will review and consider merging. Expect though that it will likely take some time to review that type of change and feel comfortable with it. |
Thanks for info. I'll get back if I come up with some solution :) |
I don't have any changes to the codebase to propose, though I'd like to share my HystrixConcurrencyStrategy implementation, maybe someone will find it useful. Because I know public class MyHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new MyCallable<>(callable);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
return new HystrixRequestVariableDefault<T>() {
@Override
public T get() {
if (!HystrixRequestContext.isCurrentThreadInitialized()) {
return null;
}
return super.get();
}
};
}
} |
the code from @pbetkier above doesn't seem to support RequestContext properly when it is initialized and does not provide the behavior of the default strategy: https://github.com/Netflix/Hystrix/blob/master/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixConcurrencyStrategy.java#L147 The This logic could potentially be reworked to provide a defined implementation of |
@mwhipple Yeah, if you're able to submit a PR, I'd be happy to review and get opinions from others in this thread. |
I'm interested in this as well. We are looking to pass tracing information through to Hystrix commands on separate threads. |
@spencergibb To clarify, the capability already exists to pass contextual data from caller threads to Hystrix threads and is described above (#92 (comment)). The work being discussed here will (potentially) provide a way to write this logic in a way that's more decoupled from the |
Thanks for the clarification @mattrjacobs. I think I'm still interested :-), but might be able to get along with the more coupled way. |
if use threadlocal to set some value,hystrix will lost it, how to resolve? |
@Dreampie You can use a concurrency strategy as described on the Wiki |
I am following this approach to forward a request header throughout the microservice call chain....all the above makes sense, but I can not see how to "access" the HystrixRequestVariable that is injected into the HystrixRequestContext in my servlet filter..
` I am currently using a custom RequestInterceptor for my Feign client as shown: '
' |
The concept of variables which span an entire request is pretty funky, though quite useful. The only way to manage this is to use a static object which you can then call In this case, you could create a new XAuthToken class with static |
Thanks..makes sense...hence all the "...Holder" classes. |
For anyone using hystrix-clojure, I create a simple library to deal with dynamic var binding in hystrix command, it's the same issue with threadlocal. |
Hi All, i had HystrixEventNotifier, and HystrixConcurrencyStrategy plugins registered. |
Hello,
I have a number of 3 REST-based micro services. Each of these belongs to a logical layer. A Web app calls the service on the upper layer and the call goes down to the second layer and finally the third one where data are obtained and get responded all the way back. I need to log a common RequestId (as a GUID) that identifies a single web request in all layers. This request id is created on the first service and as a Header it automatically passes from each http client to the next service and from the service to the next client using Client & Servlet filters and a Logback MDC instance. So, in each layer, if the Servlet filter does not get a RequestId header, it creates it, and it puts it in the MDC for the current thread. Whenever an http client request is made, the Client Filter, reads the request id from the MDC, creates a header and passes it to the request and the chain goes on.
If i use a Hystrix Command as a wrapper to the actual http client call, the chain breaks. Apparently, the threads created by a Hystrix Command are not "children" of the thread that called its constructor and therefore the MDC values do not propagate.
Is there a solution to this problem? I read about the execution hooks. Can they offer a solution?
More info at http://logback.qos.ch/manual/mdc.html#autoMDC and http://logback.qos.ch/manual/mdc.html#managedThreads
The text was updated successfully, but these errors were encountered: