Hystrix-request cache(请求缓存)
开启请求缓存
请求缓存在run()和construce()执行之前生效,所以可以有效减少不必要的线程开销。你可以通过实现getCachekey()方法来开启请求缓存。
package org.hope.hystrix.example.request.cache; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; public class CommandUsingRequestCache extends HystrixCommand<Boolean> { private final int value; public CommandUsingRequestCache(int value) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.value = value; } @Override protected Boolean run() throws Exception { return value == 0 || value % 2 == 0; } @Override protected String getCacheKey() { return String.valueOf(value); } }
单元测试
package org.hope.hystrix.example.request.cache; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; import org.hope.hystrix.example.request.cache.CommandUsingRequestCache; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class CommandUsingRequestCacheTest { @Test public void testWithoutCacheHits() { HystrixRequestContext cxt = HystrixRequestContext.initializeContext(); try { assertTrue(new CommandUsingRequestCache(2).execute()); assertTrue(new CommandUsingRequestCache(1).execute()); } finally { cxt.shutdown(); } } @Test public void testWithCacheHits() { HystrixRequestContext cxt = HystrixRequestContext.initializeContext(); try { CommandUsingRequestCache command2a = new CommandUsingRequestCache(2); CommandUsingRequestCache command2b = new CommandUsingRequestCache(2); assertTrue(command2a.execute()); // this is the first time we've executed this command with the value of "2" so it should not be from cache // assertFalse(command2a.isResponseFromCache()); System.out.println(command2a.isResponseFromCache()); assertTrue(command2b.execute()); // this is the second time we've executed this command with the same value so it should return from cache // assertTrue(command2b.isResponseFromCache()); System.out.println(command2b.isResponseFromCache()); } finally { cxt.shutdown(); } // start a new request context cxt = HystrixRequestContext.initializeContext(); try { CommandUsingRequestCache command3b = new CommandUsingRequestCache(2); assertTrue(command3b.execute()); // this is a new request context so this should not come from cache // assertFalse(command3b.isResponseFromCache()); System.out.println(command3b.isResponseFromCache()); } finally { cxt.shutdown(); } } }
清理失效缓存
package org.hope.hystrix.example.request.cache; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixRequestCache; import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault; /** * 清理缓存 */ public class CommandUsingRequestCacheInvalidation{ private static volatile String prefixStoredOnRemoteDataStore = "ValueBeforeSet_"; public static class GetterCommand extends HystrixCommand<String> { private static final HystrixCommandKey GETTER_KEY = HystrixCommandKey.Factory.asKey("GetterCommand"); private final int id; public GetterCommand(int id) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetSetGet")).andCommandKey(GETTER_KEY)); this.id = id; } @Override protected String run() throws Exception { return prefixStoredOnRemoteDataStore + id; } @Override protected String getCacheKey() { return String.valueOf(id); } //Allow the cache to be flushed for this object. public static void flushCache(int id) { HystrixRequestCache.getInstance(GETTER_KEY, HystrixConcurrencyStrategyDefault.getInstance()).clear(String.valueOf(id)); } } public static class SetterCommand extends HystrixCommand<Void> { private final int id; private final String prefix; public SetterCommand(int id, String prefix) { super(HystrixCommandGroupKey.Factory.asKey("GetSetGet")); this.id = id; this.prefix = prefix; } @Override protected Void run() throws Exception { // persist the value against the datastore prefixStoredOnRemoteDataStore = prefix; // flush the cache GetterCommand.flushCache(id); return null; } } }
单元测试:
package org.hope.hystrix.example.request.cache; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; import org.junit.Test; /** * Created by lisen on 2017/12/27. */ public class CommandUsingRequestCacheInvalidationTest { @Test public void flushCacheTest() { HystrixRequestContext context = HystrixRequestContext.initializeContext(); System.out.println(new CommandUsingRequestCacheInvalidation.GetterCommand(1).execute()); CommandUsingRequestCacheInvalidation.GetterCommand commandAgainstCache = new CommandUsingRequestCacheInvalidation.GetterCommand(1); System.out.println(commandAgainstCache.isResponseFromCache()); //false System.out.println(commandAgainstCache.execute()); System.out.println(commandAgainstCache.isResponseFromCache()); //false // set the new value new CommandUsingRequestCacheInvalidation.SetterCommand(1, "ValueAfterSet_").execute(); // fetch it again CommandUsingRequestCacheInvalidation.GetterCommand commandAfterSet = new CommandUsingRequestCacheInvalidation.GetterCommand(1); //the getter should return with the new prefix, not the value from cache System.out.println(commandAfterSet.isResponseFromCache()); System.out.println(commandAfterSet.execute()); } }
注解的实现请求缓存
注解 | 描述 | 属性 |
@CacheResult | 改注解用来标记请求命令返回的结果应该被缓存,它必须与@HystrixCommand注解结合使用 | cacheKeyMethod |
@CacheRemove | 该注解用来让请求命令的缓存失效,失效的缓存根据定义的key决定 | commandKey,cacheKeyMethod |
@CacheKey |
改注解用来在请求命令的参数上标记,使其作为缓存的Key值,如果没有标注则会使用所有参数。如果同时还使用了@CacheResult和 @CacheRemove注解的cacheKeyMethod方法指定缓存Key的生成,那么该注解将不会起作用 |
value |
设置缓存
package org.hope.hystrix.example.request.cache; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult; import org.hope.hystrix.example.model.User; import org.springframework.stereotype.Service;
@Service public class RequestCacheAnnotation { /** * 返回的结果会置入请求缓存中,缓存的key值会使用所有的方法入参, * 也就是这里Long类型的id */ @CacheResult @HystrixCommand public String getUserById(Long id) { return "你好" + id; } /** * cacheKeyMethod可以为缓存指定具体的缓存key */ @CacheResult(cacheKeyMethod = "getUserByIdCacheKey") @HystrixCommand public String getUserById2(Long id) { return "你好:" + id; } public String getUserByIdCacheKey(Long id) { return String.valueOf(id); } /** * 通过@CacheKey来定义具体的缓存Key。 * 但是注意,@CacheKey的优先级比@CacheResult(cacheKeyMethod = "")的优先级低。 * 如果已经使用了cacheKeyMethod指定缓存Key的生成函数,那么@CacheKey注解不会生效 */ @CacheResult @HystrixCommand public String getUserById3(@CacheKey("id") Long id) { return "你好:" + id; } /** * @CacheKey还可以通过访问参数对象内部属性作为缓存key * 这里指定了User对象id属性作为缓存key */ @CacheResult @HystrixCommand public String getUserById4(@CacheKey("id") User user) { return "你好" + user.getId(); } }
清理缓存
package org.hope.hystrix.example.request.cache; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove; import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult; import org.hope.hystrix.example.model.User; public class RequestClearCacheAnnotation { @CacheResult @HystrixCommand public User getUserById(@CacheKey("id") Long id) { User u = new User(); u.setId(id); return u; } /** * 用@CacheRemove来清理失效缓存,其中commandKey是必须指定的 */ @CacheRemove(commandKey = "getUserById") @HystrixCommand public void update(@CacheKey("id") User user) { User u = new User(); u.setId(20L); } public String getUserById(User user) { return String.valueOf(user.getId()); } }
https://gitee.com/huayicompany/Hystrix-learn/tree/master/hystrix-example
参考:
[1]Github,https://github.com/Netflix/Hystrix/wiki/How-it-Works
[2] 《SpringCloud微服务实战》,电子工业出版社,翟永超