Programmatically changing Hystrix properties
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
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.
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).
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
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
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.