Hystrx权威指南--Hystrix请求缓存和COLLAPSER

简介

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



 
 
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值