Programmatically changing Hystrix properties
Programmatically changing Hystrix properties
I have a circuit breaker set up that I would like to change parameters for runtime. Things like threads and timeout needs to be tuned at customer site.
I create a HystrixCommandProperties.Setter like this:
HystrixCommandProperties.Setter hystrixProps =
HystrixCommandProperties.defaultSetter()
.withCircuitBreakerSleepWindowInMilliseconds(myconf.sleepWindow);
HystrixThreadPoolProperties.Setter threadPoolSettings =
HystrixThreadPoolProperties.Setter()
.withCoreSize(myconf.threadPoolSize);
new MyCommand(HystrixCommand.Setter.withGroupKey("mygroup")
.andCommandPropertiesDefaults(hystrixProps)
.andThreadPoolPropertiesDefaults(threadPoolSettings));
MyCommand implements standard HystrixCommand and calls super(hystrixProps).
This works the first time, but when I try to change the properties at runtime (same group name) nothing happens. Is there another way to programmatically change this?
I don't want to go through the property files or specifying an URL to Archaius.
There are also answers that tells me to go through Archaius with ConfigurationManager.getConfigInstance().setProperty("...") . But surely there has to be a way that is similar to the original setters I create? Doing it completely different because it's the second time around just feels awkward.
5 Answers
5
Late answer, but today i struggled with the same thing and found a way.
The way the default property manager is implemented is that it uses a cache of HystrixCommandProperties
based on the name of the command you run. On the first use of the command, it caches what it gets out of the HystrixCommandProperties.Setter
passed to the Command's constructor and that's it.
HystrixCommandProperties
HystrixCommandProperties.Setter
However, using the custom HystrixPropertiesStrategy
as a Plugin you can override the cache key (and hence force Hystrix to re-evaluate the Setter passed to the new Command instance, because the cache key is new, so it thinks it's a new Command).
HystrixPropertiesStrategy
The code would then look similar to this:
public HystrixCommandFactory(....) {
HystrixPlugins.getInstance().registerPropertiesStrategy(new HystrixPropertiesStrategyWithReloadableCache());
updateHystrixSettings();
}
//configurable attributes
private volatile int commandTimeoutMillis;
private volatile long lastSettingsUpdatedTimestamp;
private volatile HystrixCommand.Setter setterForNewCommands;
private void updateHystrixSettings() {
lastSettingsUpdatedTimestamp = LocalDateTime.now().toDateTime().getMillis();
HystrixCommandProperties.Setter propertiesSetter = HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(commandTimeoutMillis)
.withExecutionTimeoutEnabled(true);
this.setterForNewCommands = HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(GROUP_NAME))
.andCommandPropertiesDefaults(propertiesSetter);
}
public void setCommandTimeoutMillis(int commandTimeoutMillis) {
this.commandTimeoutMillis = commandTimeoutMillis;
updateHystrixSettings();
}
private class HystrixPropertiesStrategyWithReloadableCache extends HystrixPropertiesStrategy {
@Override
public String getCommandPropertiesCacheKey(HystrixCommandKey commandKey, HystrixCommandProperties.Setter builder) {
return String.format("%s-%d", commandKey.name(), lastSettingsUpdatedTimestamp);
}
}
Alternatively, you could always return null
from the getCommandPropertiesCacheKey
method (that completely turns off caching), but then you have the overhead of Hystrix having to reconstruct the HystrixCommandProperties
each time a Command is called
null
getCommandPropertiesCacheKey
HystrixCommandProperties
PS: Be sure to use proper thread synchronization for reading and updating these properties, because those will be called from different threads. I omitted that in this sample for simplicity, but I actually use a ReentrantReadWriteLock
in my code to guard accesses to these variables
ReentrantReadWriteLock
Hystrix properties can also be set in our service class inside
@HystrixCommand annotation, for this we use the Hystrix-Javanica
project which is used for implementing the annotations in our project.
For that we need to add the dependency of hystrix-javanica into our
classpath.
Dependency for Maven:
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>x.y.z</version>
</dependency>
Inside the @HystrixCommand annotation we can use @HystrixProperty to set the properties for hystrix.
sample @HystrixCommand properties setting:
@HystrixCommand(groupKey = "StoreSubmission", commandKey = "StoreSubmission", threadPoolKey = "StoreSubmission", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "30000"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "4"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "60000"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "180000") }, threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "180000") })
public String storeSubmission(ReturnType returnType, InputStream is, String id) {
}
The best way to define these properties is in externalized application.yaml, that way you can control it better & change them for different environments.
Here is the sample hystrix config in my application.yaml
hystrix:
command.StoreSubmission.execution.isolation.thread.timeoutInMilliseconds: 30000
command.StoreSubmission.circuitBreaker.requestVolumeThreshold: 4
command.StoreSubmission.circuitBreaker.sleepWindowInMilliseconds: 60000
command.StoreSubmission.metrics.rollingStats.timeInMilliseconds: 180000
collapser.StoreSubmission.maxRequestsInBatch: 1
collapser.StoreSubmission.requestCache.enabled: FALSE
threadpool.StoreSubmission.coreSize: 30
threadpool.StoreSubmission.metrics.rollingStats.timeInMilliseconds: 180000
The Exact format for the application.yml file is
hystrix:
command:
findAllProducts:
execution:
isolation:
thread:
timeoutInMilliseconds: 1000
circuitBreaker:
requestVolumeThreshold: 20
errorThresholdPercentage: 50
metrics:
rollingStats:
timeInMilliseconds: 10000
numBuckets: 10
threadpool:
ProductService:
coreSize: 10
For further information on Hystrix-Javanica visit here
For future reference: I ended up using the settings through ConfigurationManager and a string property.
ConfigurationManager.getConfigInstance().setProperty("...")
It let's me change things, but in a less type-safe way than the original code. I did struggle for some time with a typo in the string which is why I'd like to avoid that.
I now use this for all the properties I need to change runtime. Creating a new Hystrix circuit breaker every time something changes (new command key) could be an option as well, but would break using properties files later on.
I am facing a similar issue - once i create a first HystrixCommand with a Setter in a certain group, i can't change the properties later by creating a new HystrixCommand with a different Setter (in the same group). So is it indeed the case that hystrix is somehow "caching" the properties from the first Setter it gets and, except for using ConfigurationManager, there really isn't a way to change the properties?
– Michal Boska
May 22 '17 at 15:30
I finally found a solution after 3 hours when neither HystrixPropertiesStrategy nor PolledConfigurationSource worked.
Here is what worked for me
HystrixThreadPoolMetrics.getInstance(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")).getThreadPool().setCorePoolSize(threadCount);
Still need to check if it is right solution or not though.
There is a very simple way of doing this. It just needs 2 steps:
a. registering the right plugin
b. Adding the correct strategy with the required override.
Use-case: Override ExecutionTimeoutInMilliseconds to 30000 ms from 1000 ms
HystrixPlugins.getInstance().registerPropertiesStrategy(new HystrixPropertiesStrategy() {
@Override
public HystrixCommandProperties getCommandProperties(HystrixCommandKey commandKey, HystrixCommandProperties.Setter builder) {
HystrixCommandProperties.Setter timeout
= HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(30000);
return super.getCommandProperties(commandKey, timeout);
}
});
Here I am just overriding the required property. When you run your application you can see this in the DEBUG mode:
2018-06-08 23:18:32 [main] DEBUG c.n.h.s.p.HystrixPropertiesChainedProperty - Flipping property: hystrix.command.Client#getAllData().execution.isolation.thread.timeoutInMilliseconds to use its current value: 30000
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
I need to change the properties at runtime programmatically, so you're not really answering my quesiton. Javanica is nice, but we're not having people change params through files. I could theoretically write to a file from code, but that's even more of a hassle and indirect way than what I'm doing now: ConfigurationManager.getConfigInstance().setProperty("...") . I just would like to use a strongly typed API for changing the properties after first initialisation like I did in the first place. Strings and possible typos/misunderstandings just gets in the way. As it did when i switched to it...
– Anders S
Nov 10 '16 at 8:55