简介

在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是不支持跨线程。