简介
在Hystrix中有个Request的概念,有一些操作需要在request中进行
缓存
在Hystrix调用服务时,如果只是查询接口,可以使用缓存进行优化,从而跳过真实访问请求。
应用
需要启用缓存的话需要重写command中getCacheKey方法
@Override
protected String getCacheKey() {
return String.valueOf(value);
}
之后就可以调用了
但是如果直接调用command的运行相关方法会得到以下错误
Caused by: java.lang.IllegalStateException: Request caching is not available. Maybe you need to initialize the HystrixRequestContext?
这里说需要初始化HystrixRequestContext,在Hystrix中带缓存的command是必须在request中调用的。
public void testWithCacheHits() {
//初始化context
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
//两个一样的command
CommandUsingRequestCache command2a = new CommandUsingRequestCache(2);
CommandUsingRequestCache command2b = new CommandUsingRequestCache(2);
assertTrue(command2a.execute());
// 第一次调用的返回是否从cache获得是false
assertFalse(command2a.isResponseFromCache());
assertTrue(command2b.execute());
// 第而次调用的返回是否从cache获得是true
assertTrue(command2b.isResponseFromCache());
} finally {
//关闭context
context.shutdown();
}
}
在不同context中的缓存是不共享的,还有这个request内部一个ThreadLocal,所以request只能限于当前线程
更新缓存
如果服务同时有更新的操作,那么在request中需要将原先的缓存失效
public static class GetterCommand extends HystrixCommand<String> {
//getter key 用于命名当前command,之后清楚缓存时需要
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() {
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静态方法中找到对应实例,并将响应值清除
HystrixRequestCache.getInstance(GETTER_KEY,
HystrixConcurrencyStrategyDefault.getInstance()).clear(String.valueOf(id));
}
}
Collapser
Collapser可以合并多个请求一起调用
//需要定义一个Collapser
public class CommandCollapserGetValueForKey extends HystrixCollapser<List<String>, String, Integer> {
private final Integer key;
public CommandCollapserGetValueForKey(Integer key) {
this.key = key;
}
@Override
public Integer getRequestArgument() {
return key;
}
@Override
protected HystrixCommand<List<String>> createCommand(final Collection<CollapsedRequest<String, Integer>> requests) {
return new BatchCommand(requests);
}
@Override
protected void mapResponseToRequests(List<String> batchResponse, Collection<CollapsedRequest<String, Integer>> requests) {
int count = 0;
for (CollapsedRequest<String, Integer> request : requests) {
request.setResponse(batchResponse.get(count++));
}
}
//底层command实现
private static final class BatchCommand extends HystrixCommand<List<String>> {
private final Collection<CollapsedRequest<String, Integer>> requests;
private BatchCommand(Collection<CollapsedRequest<String, Integer>> requests) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GetValueForKey")));
this.requests = requests;
}
//内部还是遍历入参进行调用
@Override
protected List<String> run() {
ArrayList<String> response = new ArrayList<String>();
for (CollapsedRequest<String, Integer> request : requests) {
// artificial response for each argument received in the batch
response.add("ValueForKey: " + request.getArgument());
}
return response;
}
}
}
@Test
public void testCollapser() throws Exception {
//初始化request
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
//直接调用collapser
Future<String> f1 = new CommandCollapserGetValueForKey(1).queue();
Future<String> f2 = new CommandCollapserGetValueForKey(2).queue();
Future<String> f3 = new CommandCollapserGetValueForKey(3).queue();
Future<String> f4 = new CommandCollapserGetValueForKey(4).queue();
assertEquals("ValueForKey: 1", f1.get());
assertEquals("ValueForKey: 2", f2.get());
assertEquals("ValueForKey: 3", f3.get());
assertEquals("ValueForKey: 4", f4.get());
//只调用了一次command
// assert that the batch command 'GetValueForKey' was in fact
// executed and that it executed only once
assertEquals(1, HystrixRequestLog.getCurrentRequest().getExecutedCommands().size());
HystrixCommand<?> command = HystrixRequestLog.getCurrentRequest().getExecutedCommands().toArray(new HystrixCommand<?>[1])[0];
// assert the command is the one we're expecting
assertEquals("GetValueForKey", command.getCommandKey().name());
// confirm that it was a COLLAPSED command execution
assertTrue(command.getExecutionEvents().contains(HystrixEventType.COLLAPSED));
// and that it was successful
assertTrue(command.getExecutionEvents().contains(HystrixEventType.SUCCESS));
} finally {
//关闭request
context.shutdown();
}
}
Collapser可以用来一起调用一批请求,在第一个调用get时会对积压的command一起调用。
总结
Hystrix的Request主要就是cache和collapser用到。在一般web应用中可以在filter中加入context的初始化和关闭逻辑。还要注意request是不支持跨线程。