如何使用Hystrix实现服务请求缓存?

分类: Java

保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/132/ )。

概述

Hystrix支持在方法层面增加缓存,这样,当下次在请求该方法时,如果存在缓存,则可以直接走缓存而不用执行方法。

环境配置

通过Hystrix就可以方便地实现这一操作。接下来,我们给出简单的示例。

首先,准备SpringBoot项目,然后在其中引入适用于SpringBoot的hystrix包:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>

注意,如果这时候启动提示如下的错误,则基本是SpringBoot版本与hystrix版本不匹配导致的。因为hystrix是基于SpringCloud的,而SpringCloud和SpringBoot存在版本对应关系。如果对应不一致,则可能导致出现下面的错误。

java.lang.NoSuchMethodError: 'void org.springframework.boot.builder.SpringApplicationBuilder.<init>(java.lang.Object[])'

这里我们给出一个可用的对应版本,即SpringBoot的2.3.5.RELEASE和spring-cloud-starter-netflix-hystrix的2.2.7.RELEASE。它们这一对是肯定没有问题的。

然后,我们需要在SpringBoot的主类上增加@EnableHystrix注解,以启用Hystrix。如下所示。

@SpringBootApplication
@EnableHystrix
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

环境配置就是这么简单,接下来我们就可以写业务代码了。

功能实现

缓存时,默认会将所有的入参共同作为缓存的键,也就是说下次调用时只有全部入参完全一致才会走缓存。另外,我们也可以使用@CacheKey注解来指定缓存的键。此外,我们还可以指定一个方法,以该方法的返回值作为缓存的键。这种通过方法指定键的方式具有最高的优先级。要注意的是,这个方法必须和原方法在同一个类中,且入参与原方法完全一致、返回值必须为String。

为了能够清除缓存,我们还可以指定一个缓存清理方法。当调用该方法时,指定方法的缓存将会被清理。

我们先给出请求缓存的示例代码:

@CacheResult(cacheKeyMethod = "getUserCacheKey")
@HystrixCommand(
        commandKey = "CacheService_queryUserNameById",
        commandProperties = {
                // 启用请求缓存功能(默认也是启用的)
                @HystrixProperty(name = HystrixPropertiesManager.REQUEST_CACHE_ENABLED, value = "true"),
        }
)
String queryUserNameById(int i) {
    System.out.println("正在查询用户名,用户编号为" + i);
    return "易哥" + i;
}

@CacheRemove(commandKey = "CacheService_queryUserNameById",
        cacheKeyMethod = "getUserCacheKey")
@HystrixCommand
void clearUserResult(int i) {
    System.out.println("入参" + i + "对应的缓存已清空 ");
}

String getUserCacheKey(int i) {
    return "CACHE_KEY_" + i;
}

在介绍上述示例代码的同时,我们顺便详细介绍两个注解。注解的介绍参考《高性能架构之道(第二版)》这本书,本文的示例代码也来自这本书。大家如果想要系统学习Hystrix的话,建议读下该书。

  • @CacheResult注解,加在要进行请求缓存的方法上。它只有一个属性cacheKeyMethod用来指定要为缓存生成键的方法。默认为空字符串,即使用该方法的全部参数作为缓存的键或者按照@CacheKey注解来指定缓存的键。在该示例中,我们指定了getUserCacheKey方法来生成缓存的键。
  • @CacheRemove注解,加在用以清理缓存的方法上。它有两个属性。
    • commandKey参数用来指定是要清理哪个缓存。在本示例中,是要清理指令名为CacheService_queryUserNameById的缓存,这正是queryUserNameById方法的指令名。
    • cacheKeyMethod参数用来指定清理缓存时,要清理的键由哪个方法生成。要指向和@CacheResult注解一样的方法才可,否则无法达到预期的清理效果。

了解了以上内容后,我们就可以通过下面的代码对上述方法进行调用:

StringBuilder stringBuilder = new StringBuilder();
HystrixRequestContext context = HystrixRequestContext.initializeContext();
for (int i = 1; i <= 3; i++) {
    stringBuilder.append(cacheService.queryUserNameById(303)).append("\r\n");
    stringBuilder.append(cacheService.queryUserNameById(316)).append("\r\n");
    stringBuilder.append(cacheService.queryUserNameById(808)).append("\r\n");
    cacheService.clearUserResult(808);
}
context.close();
return stringBuilder.toString();

控制台上可以看到如下的输出:

正在查询用户名,用户编号为303
正在查询用户名,用户编号为316
正在查询用户名,用户编号为808
入参808对应的缓存已清空 
正在查询用户名,用户编号为808
入参808对应的缓存已清空 
正在查询用户名,用户编号为808
入参808对应的缓存已清空 

可见,缓存和清理功能均可正常生效。

参数详解

以上实现的是最简单的缓存示例,也仅仅使用了最简单的参数,而其他参数都是用的默认值。但是这个作为示例是可以的,真正使用时,大家还是要对Hystrix的各个参数有详细的了解,至少也要知道大体的含义和默认值。否则,后续使用中,可能会遇到不少百思不得其解的地方。

总之,Hystrix要比这个强大的多。大家还是要好好学一下。

下面是我对@HystrixCommand参数的介绍,主要是摘抄参考了《高性能架构之道(第二版)》这本书。上面的示例也是参考的书里的。下面的参数我这里列的不是很全,如果要细致学习的话,十分建议大家去看下这本书。

  • groupKey 组名。通过为命令指定一个组名,我们就可以将多个指令分到同一个组中。这样,我们可以使用配置文件为该组设置参数,而不用在每个指令中一一设定参数。通过组名,我们也可以对指标汇总分类。默认为该注解所处类的类名。
  • commandKey 指令名。用来唯一指定一个指令。这样,我们可以使用配置文件为某个指令设置参数,也可以在指标展示时知道指标具体来自哪个指令。默认为该注解所在的方法的方法名。如果程序中注解所在方法的名字不是唯一的,那强烈建议通过commandKey指定一个唯一的名字,以防止多个指令间配置信息的互相干扰。
  • fallbackMethod 备用方法名。用来指定该方法的备用方法,当该方法发生错误或超时时,将执行该备用方法。注意,备用方法必须和该方法有相同的入参类型和返回值类型。
  • commandProperties 命令参数。在这里可以为该指令设置多个命令参数。具体命令参数的介绍详见书中的命令参数小节。
  • ignoreExceptions 要忽略的异常。通常,当原方法执行抛出异常时,Hystrix会自动帮我们调用备用方法。通过该参数指定一些异常,就可以让Hystrix遇到这些异常时,将它们抛出而不触发备用方法。
  • raiseHystrixExceptions 异常包装设置。该配置只有两个选项,要么为空,要么填写RUNTIME_EXCEPTION。如果填写RUNTIME_EXCEPTION,那原方法执行中抛出的所有未被忽略(参见ignoreExceptions配置)的异常会被包装进HystrixRuntimeException中抛出。因为有些场景下,外部调用方需要捕获的异常的类型是相同的,以便于处理。默认值为空,这种情况下原方法的异常会被正常抛出而不被包装。
  • defaultFallback 默认备用方法。它比备用方法(参见fallbackMethod配置)的优先级更低,因此只在没有配置备用方法的时候生效。默认备用方法不允许有入参,且返回值类型必须要和原方法一致。

好了,以上就是Hystrix实现请求方法缓存的简单示例。

可以访问个人知乎阅读更多文章:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。

作者书籍推荐

作者书籍推荐