開啟請求緩存
請求緩存在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微服務實戰》,電子工業出版社,翟永超