Caching Netflix Hystrix Request Executions Example
In this tutorial we show you how to cache Hystrix
request executions. We use the HystrixCommand
which is used to wrap code that will execute potentially risky functionality. Typically meaning a service call over the network with fault and latency tolerance. We can cache subsequent requests by providing a cacheKey
.
Project Structure
Let’s start by looking at the project structure.
Maven Dependencies
We use Apache Maven to manage our project dependencies. Make sure the following dependencies reside on the class-path.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.netflix.hystrix</groupId>
<artifactId>request-cahce</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>https://memorynotfound.com</url>
<name>Netflix Hystrix - ${project.artifactId}</name>
<dependencies>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.3</version>
</dependency>
<!-- logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>
<!-- testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Caching Netflix Hystrix Request Executions Example
The getCacheKey()
method registers a key to be used for request caching. By default this method returns null which means “do not cache”. To enable caching override this method and return a string key uniquely representing the state of a command instance. If multiple command instances in the same request scope match keys then only the first will be executed and all others returned from cache.
package com.memorynotfound.netflix.hystrix;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
public class CircleAreaCalculatorCommand extends HystrixCommand<Double> {
private final Double radius;
public CircleAreaCalculatorCommand(Double radius) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.radius = radius;
}
@Override
protected Double run() throws Exception {
return Math.PI * (radius * radius);
}
@Override
protected String getCacheKey() {
return String.valueOf(radius);
}
}
Writing JUnit Test Caching Hystrix Request
The HystrixRequestContext
contains the state and manages the lifecycle. We start the context using the initializeContext()
method. This’ll initialize hystrix with request scoped caching.
In the testWithoutCacheHits
JUnit test we execute two HystrixCommand
commands with different arguments. Note that each command returns a response without hitting the cache.
In the second JUnit test testWithCacheHits
we execute two HystrixCommand
commands with the same argument. Not that the second method returns a cached response. After this, we reset the context by using the HystrixRequestContext.initializeContext()
and execute the same command.
package com.memorynotfound.netflix.hystrix.test;
import org.junit.Test;
import com.memorynotfound.netflix.hystrix.CircleAreaCalculatorCommand;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import static junit.framework.TestCase.*;
public class CircleAreaCalculatorCommandTest {
@Test
public void testWithoutCacheHits() throws Exception {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
CircleAreaCalculatorCommand command1 = new CircleAreaCalculatorCommand(2.11);
assertEquals(13.986684653047117, command1.execute());
assertFalse(command1.isResponseFromCache());
CircleAreaCalculatorCommand command2 = new CircleAreaCalculatorCommand(5.32);
assertEquals(88.91461191895976, command2.execute());
assertFalse(command2.isResponseFromCache());
} finally {
context.shutdown();
}
}
@Test
public void testWithCacheHits() throws Exception {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
CircleAreaCalculatorCommand command1 = new CircleAreaCalculatorCommand(13.13);
CircleAreaCalculatorCommand command2 = new CircleAreaCalculatorCommand(13.13);
assertEquals(541.6008345416543, command1.execute());
assertFalse(command1.isResponseFromCache());
assertEquals(541.6008345416543, command2.execute());
assertTrue(command2.isResponseFromCache());
} finally {
context.shutdown();
}
// start a new request context
context = HystrixRequestContext.initializeContext();
try {
CircleAreaCalculatorCommand command3 = new CircleAreaCalculatorCommand(13.13);
assertEquals(541.6008345416543, command3.execute());
assertFalse(command3.isResponseFromCache());
} finally {
context.shutdown();
}
}
}
JUnit Test Results
When we run all of our JUnit tests, we receive the following output.
References
- Netflix Hystrix Documentation
- Netflix Hystrix JavaDoc API
- HystrixCommand JavaDoc
- HystrixRequestContext JavaDoc