Spring cloud Hystrix 服务容错保护---断路器(3)- 请求缓存

Hystrix有两种方式来应对高并发场景:请求缓存和请求合并。

请求缓存:当系统并发量越来越大,每个微服务都要承受很大的压力,因为请求依赖服务的资源需要通过通信来实现,如果每一此都去服务提供者的服务器去请求服务,获得数据,那么高并发的情况下,性能会低下,Hystrix提供了请求缓存功能,我们可以开启请求缓存功能来优化系统,以便减轻请求线程消耗和降低 请求响应时间。

 

开启请求缓存:

继承的方式:

public class HelloCommand extends HystrixCommand<String> {

private static final HystrixCommandGroupKey GROUP_KEY=HystrixCommandGroupKey.Factory.asKey("testgroupKey");

    private static final HystrixCommandKey COMMAND_KEY=HystrixCommandKey.Factory.asKey("testCommandKey");

    private static final HystrixThreadPoolKey THREAD_POOL_KEY=HystrixThreadPoolKey.Factory.asKey("testThreadKey");

    private RestTemplate restTemplate;

    public HelloCommand( RestTemplate restTemplate) {
        super(Setter.withGroupKey(GROUP_KEY).andCommandKey(COMMAND_KEY).andThreadPoolKey(THREAD_POOL_KEY));
        this.restTemplate=restTemplate;
    }

    @Override
    protected String run() throws Exception {
        return restTemplate.getForObject("http://helloservice-1",String.class);
    }

    @Override
    protected String getFallback() {
        return "error";
    }

    @Override
    protected String getCacheKey() {
        return "test";
    }

}

只需要重载getCacheKey方式,通过在 getCacheKey 方法中返回的请求缓存 key 值,就能让该请求命令具备缓存功能。 此时,当不同的外部请 求处理逻辑调用了同 一 个依赖服务时, Hystrix 会根据 getCacheKey 方法返回的值来区分 是否是重复的请求,如果它们的 cacheKey 相同, 那么该依赖服务只会在第一个请求到达时 被真实地调用一 次, 另外 一 个请求则是直接从请求缓存中返回结果。

如果只是读操作,那么不需要考虑缓存内容是否正确,但是如果请求命令中还有更新数据的写操作,那么缓存中的数据就需要我们在进行写操作是做及时的处理,以防止读操作读到脏数据。

我们可以通过 HystrixRequestCache.clear() 方法来进行缓存的 清理:

public class HelloCommand extends HystrixCommand<String> {

    private static final HystrixCommandGroupKey GROUP_KEY=HystrixCommandGroupKey.Factory.asKey("testgroupKey");

    private static final HystrixCommandKey COMMAND_KEY=HystrixCommandKey.Factory.asKey("testCommandKey");

    private static final HystrixThreadPoolKey THREAD_POOL_KEY=HystrixThreadPoolKey.Factory.asKey("testThreadKey");

    private RestTemplate restTemplate;

    public HelloCommand( RestTemplate restTemplate) {
        super(Setter.withGroupKey(GROUP_KEY).andCommandKey(COMMAND_KEY).andThreadPoolKey(THREAD_POOL_KEY));
        this.restTemplate=restTemplate;
    }

    @Override
    protected String run() throws Exception {
        return restTemplate.getForObject("http://helloservice-1",String.class);
    }

    @Override
    protected String getFallback() {
        return "error";
    }

    @Override
    protected String getCacheKey() {
        return "test";
    }

    public static void flushCache(){
        HystrixRequestCache.getInstance(COMMAND_KEY,HystrixConcurrencyStrategyDefault.getInstance()).clear("test");
    }
}

1.其中getInstance方法中的第一个参数的key名称要与实际相同 *

2.clear方法中的cacheKey要与getCacheKey方法生成的key方法相同 *

3.注意我们用了commandKey是test,大家要注意之后new这个Command的时候要指定相同的commandKey,否则会清除不成功 *

 

在执行写操作的之后,就调用这个flushCache方法对缓存进行清理。

请求缓存不是只写入一次结果就不再变化的,而是每次请求到达Controller的时候,我们都需要为HystrixRequestContext进行初始化,之前的缓存也就是不存在了,我们是在同一个请求中保证结果相同,同一次请求中的第一次访问后对结果进行缓存,缓存的生命周期只有一次请求!

需要注意的地方:

1.flushRequestCache(),其中.clear()中的cacheKey的生成方法相同,只有把正确需要清除的key清掉才会连同value一同清掉,从而达到清除缓存的作用。

2.清除缓存时机:我们应该在同一个Controller中进行写操作之后,如果这个操作之后还有访问同一资源的请求,那么必须加清除缓存,从而保证数据同步,如果后面没有读操作,无须清除缓存,因为在下一次请求到来的时候HystrixRequestContext会重置,缓存自然也没有了

 

注解的方式:

注解描述属性
@CacheResult该注解用来标记请求命令返回的结果应该被缓存,它必须与@HystrixCommand注解结合使用cacheKeyMethod
@CacheRemove该注解用来让请求命令的缓存失效,失效的缓存根据commandKey进行查找。commandKey,cacheKeyMethod
@CacheKey该注解用来在请求命令的参数上标记,使其作为cacheKey,如果没有使用此注解则会使用所有参数列表中的参数作为cacheKeyvalue
@CacheResult
@HystrixCommand
public User getUserByid(Long id) {
return restTemplale.getForObject("http://USER-SERVICE/users/{l}", User. class, id);
}

上面的方式会用所有的参数作为cacheKey,也就是上面的id

 

当使用注解来定义请求缓存时,若要为请求命令指定具体的缓存 Key 生 成规则, 我们可以使用@CacheResut和@CacheRemove 注解的cacheKeyMethod方法来指定具体的生成函数;也可以通过使用@CacheKey 注解 在方法参数中指定用于组装缓存 Key 的元素.

@CacheResult(cacheKeyMethod = "getUserByidCacheKey") 
@HystrixCommand
public User getUserByid{Long id) {
    return restTemplale.getForObject("http://USER-SERVICE/users/{1}", User.class, id);
}

private Long getUserByidCacheKey(Long id) { 
    return id;
}

通过@CacheKey 注解实现的方式更加简单, 具体示例如下所示。 但是在使用 @CacheKey 注解的时候需要注意,它的优先级比 cacheKeyMethod 的优先级低,如果已 经使用了 cacheKeyMethod 指定缓存 Key 的生成函数, 那么@CacheKey 注解不会生效。

@CacheResult
@HystrixCommand
public User getUserByid(@CacheKey("id") Long id) {
    return restTemplate.getForObject("http://USER-SERVICE/users/{l}",User.class,id);
}

@CacheKey 注解除了可以指定方法参数作为缓存 Key 之外, 它还允许访问参数对象 的内部属性作为缓存 Key。比如下面的例子,它指定了 User 对象的 id 属性作为缓存 Key。

@CacheResult
@HystrixCommand
public User getUserByid(@CacheKey("id") User user) {
    return restTemplate.getForObject("http://USER-SERVICE/users/ { 1} ", User.class, user.getid());
}

若该内容调用了 update 操作进行了更新,那么此时请求缓 存中的结果与实际结果就会产生不一 致(缓存中的结果实际上已经失效了),所以我 们需要在 update 类型的操作上对失效的缓存进行清理。 在 Hystrix 的注解配置中, 可以通过@CacheRemove 注解来实现失效缓存的清理.

@CacheResult
@HystrixCommand(commandkey="getUserByid")
public User getUserByid(@CacheKey("id") Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/ { 1} ",id);

@CacheRemove(commandKey = "getUserByid") 
@HystrixCommand
public void update(@CacheKey("id") User user) {
    return restTemplate.postForObject("http://USER-SERVICE/users", user, User.class);
}

还可以直接在一个空方法中清理缓存:

@HystrixCommand
    @CacheRemove(commandKey = "hello" ,cacheKeyMethod = "getCacheKey")
    public void flushCache(){
        System.out.println("缓存清理。。。。。。。");
    }

controller:

@Autowired
    private HelloService helloService;
    @RequestMapping(value = "/consumer")
    public String getvalue(){
        HystrixRequestContext.initializeContext();
        //初始化HystrixRequestContext上下文
        String s1=helloService.helloServiceAysc();
        String s2=helloService.helloServiceAysc();
        helloService.flushCache();
        String s3=helloService.helloServiceAysc();
        String s4=helloService.helloServiceAysc();
        return helloService.helloService();
    }

前面提到过,下一次请求到来的时候HystrixRequestContext会重置,缓存自然也没有了。

初始化HystrixRequestContext方法:

1、在每个用到请求缓存的Controller方法的第一行加上如下代码:

HystrixRequestContext.initializeContext();

2、使用filter:

在启动类加入@ServletComponentScan注解,

@WebFilter(filterName = "hystrixRequestContextServletFilter",urlPatterns = "/*",asyncSupported = true)
public class HystrixEquestContextInitFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //初始化Hystrix请求上下文
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
            //请求正常通过
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            //关闭Hystrix请求上下文
            context.shutdown();
        }
    }
    @Override
    public void destroy() {

    }
}

结束语:只看书来学习,很有可能因为版本不对,导致出现一些问题,还是要动手实践,、

参考:https://www.cnblogs.com/hellxz/p/9056806.html

https://github.com/HellxZ/SpringCloudLearn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值