美文网首页
Hystrix 使用指南(1):基本使用

Hystrix 使用指南(1):基本使用

作者: 编走编想 | 来源:发表于2017-08-19 14:07 被阅读755次

    一、前言

    现在分布式系统的规模不断增加,对可用性的要求也越来越高。在各种高可用设计模式中,熔断、隔离、降级是经常被使用的。而相关的技术,如 Hystrix,便成为最近的热点。

    从16年初开始,我所在的团队便开始应用 Hystrix。为了进一步推动系统可用性升级,普及 Hystrix 在团队内的使用,我便有了写一系列 Hystrix 相关的文章的想法。

    虽然 Hystrix 有着很详细的官方文档,但出于语言和更多细节介绍、实践建议的考虑,还是需要一些文章作为补充。

    二、什么是 Hystrix

    虽然 Hystrix 已算不上什么新技术,但还是要简单介绍一下。

    Hystrix 是一个在调用端上,实现断路器模式,以及隔舱模式,通过避免级联故障,提高系统容错能力,从而实现高可用设计的一个 Java 类库。

    上面这段介绍是我的一个简要总结。更具体的介绍可以参照官网

    三、Hystrix 的基本使用

    Hystrix 的主要目的是保护跨进程调用,避免因为超时等问题,导致的级联故障。Hystrix 的实现方法是封装跨进程调用。具体的使用方式有多种:从编程方式看可分为编程方式和注解方式两种;从调用方式看可分为同步调用方式、异步调用方式和反应式调用方式三种。

    我们先来看最常见的同步编程方式:

    代码示例

    @Component
    public class AuthService {
      @Autowired
      private UserService userService;
    
      public boolean validateUser(String userId) {
        User user = new GetUserCommand(userId).execute(); // 6
        if (user == null) {
          return false;
        } else {
          return user.isValid();
        }
      }
    
      class GetUserCommand extends HystrixCommand<User> { // 1
        private Long userId;
    
        public GetUserCommand(Long userId) { // 2
          super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("UserService")) // 3
              .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
                  .withCoreSize(20) // 3
              )
              .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                  .withExecutionTimeoutInMilliseconds(100) // 3
              )
          );
          this.userId = userId;
        }
    
        @Override
        public User run() throws Exception {
          return userService.getUserById(userId); // 4
        }
    
        @Override
        public User getFallback() {
          return new InvalidUser(); // 5
        }
      }
    }
    

    代码解释

    1. Hystrix 常见的使用方法是在一个业务处理类(在本例中是 AuthService)新建一个内部类(本例中是 GetUserCommand)。这个内部类需要扩展 HystrixCommand。之所以使用内部类是因为 Hystrix 通常用来封装一次远程调用,一般是直接调用一个业务方法。这个业务方法通常位于一个业务处理类或这个业务处理类所依赖的类中。所以使用内部类的方式可以简化这种调用。扩展 HystrixCommand 还需声明一个泛型类型,这个泛型类型表示这个 HystrixCommand 的返回值。
    2. 定义一个 HystrixCommand 还需定义一个构造函数。这个构造函数十分重要,因为在使用这个 HystrixCommand 时,需要通过构造函数传递参数。
    3. 在构造函数中,需要调用父构造函数对当前的 HystrixCommand 进行配置。主要的配置主要有三个:GroupKey、ThreadPoolSize 和 Timeout。具体的配置方式有多种,较常用的一种方式是通过一个名为 Setter 的 Builder 类进行配置。
      1. GroupKey 通过 Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("UserService")) 语句进行配置;
      2. ThreadPoolSize 通过 .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(20)) 语句进行配置;
      3. Timeout 通过 .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100) 语句进行配置;
    4. 通过实现 run() 方法,在其中实现业务逻辑。通常是调用外部类的方法或外部类依赖的方法。
    5. 通过实现 getFallback() 方法,实现失败逻辑,可以在其中实现降级等功能。
    6. 编写完 GetUserCommand 之后,使用的时候每次都需要 new 一个新对象,再调用 execute() 方法。注意,不要调用 run() 方法,否则熔断、隔离等功能是不生效的。

    四、Hystrix 的基本配置

    上面的部分介绍了 HystrixCommand 的基本使用方法,但只是简单介绍了几个配置。所以,下面将对 HystrixCommand 的相关配置的作用做一个较为详细的介绍。

    配置模式

    Hystrix 的配置有三个维度:全局默认配置、Instance 默认配置、Instance 动态配置。除了少部分配置项外,大部分配置都支持动态修改。接下来介绍一下一些主要参数的 Instance 默认配置方式。这种配置方式也是使用 Hystrix 最先接触到的配置方式。

    Command Group

    GroupKey 是 HystrixCommand 不可缺少的配置,其它配置均为可选。所以,使用 Hystrix 可以使用下面的代码:

    public class CommandHelloWorld extends HystrixCommand<String> {
      private final String name;
    
      public CommandHelloWorld(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.name = name;
      }
    
      @Override
      protected String run() {
        return "Hello " + name + "!";
      }
    }
    

    HystrixCommandGroupKey 是一个接口,所以除了可以使用 HystrixCommandGroupKey.Factory.asKey("ExampleGroup") 的方式定义以外,也可以直接实现这个接口。比如使用如下的方式:

    public enum Groups implements HystrixCommandGroupKey {
      GROUP_1
    }
    
    class EnumGroupCommand extends HystrixCommand<String> {
      EnumGroupCommand() {
        super(Groups.GROUP_1);
      }
    
      @Override
      protected String run() throws Exception {
        LOGGER.info("Thread of Command: {}", Thread.currentThread().getName());
        return null;
      }
    }
    

    如上面代码这样,使用自定义的枚举类,实现 HystrixCommandGroupKey 接口,可以统一 Hystrix Command Group 的定义,简化配置。

    HystrixCommandGroupKey 的作用主要有两个:

    1. 一是起到分组监控、报警的作用。后面的文章会对监控等方面进行介绍;
    2. 二是在不配置 HystrixThreadPoolKey 的情况下,起到分组线程池的作用。即默认使用 HystrixCommandGroupKey 去命名线程池。使用同一个 HystrixCommandGroupKey 且没有自定义 HystrixThreadPoolKeyHystrixCommand 将使用同一个线程池。

    Command Thread-Pool

    虽然 HystrixCommandGroupKey 可以起到隔离线程池的作用,但是无法起到对线程池进行精细配置的作用。所以这里就需要线程池进行配置:

    Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyService"))
        .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("MyThreadPool"))
        .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
            .withCoreSize(10)
            .withKeepAliveTimeMinutes(1)
            .withMaxQueueSize(-1)
        )
    )
    

    andThreadPoolPropertiesDefaults 配置中的数值表示的是默认值。接下来逐项介绍:

    1. andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("MyThreadPool")) 这是配置 ThreadPoolKey。如果需要在同一个 GroupKey 下面配置不同的 ThreadPool 就需要这个配置。
    2. withCoreSize(10) 用来配置线程池大小。Hystrix 对线程池的配置有一些限制,这里只能配置线程数的 Core Size,不能配置 Max Size。不配置的话使用的默认值是 10。
    3. withKeepAliveTimeMinutes(1) 用来配置核心线程数空闲时 keep alive 的时长,默认为 1 mins。这项配置一般不需要修改。
    4. withMaxQueueSize(-1) 用来配置线程池任务队列的大小,默认值为 -1。当使用 -1 时,SynchronousQueue 将被使用,即意味着其实这个队列只是一个交换器,任务将被直接交给工作线程处理。如果工作线程不足,那任务将被拒绝;如果使用任何正整数,LinkedBlockingQueue 将被使用。

    Command Properties

    这部分是和命令执行直接相关的配置,包括隔离策略、超时时间、Fallback 相关配置。接下来介绍几个主要的配置:

    隔离策略

    默认的隔离策略是实现线程池隔离,另外一种隔离策略是 Semaphore。Instance 默认配置可使用如下方法设置:

    HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
    

    这项配置通常不用配置

    超时时间

    默认时间是1s,单位是毫秒。Instance 默认配置可以使用如下方法设置:

    .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100))
    

    这项配置比较重要,后文还会详细介绍如何调配这个参数。

    Fallback 并发度

    在 Instance 默认配置中是通过如下代码设置的:

    super(Setter.withGroupKey(BASIC_USAGE_GROUP)
        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
            .withFallbackIsolationSemaphoreMaxConcurrentRequests(10)
        )
    );
    

    默认值为 10。因为 getFallback() 方法是和 run() 方法使用同一个线程池执行的,并且不受线程超时时间限制,并发过高会影响主逻辑的执行,所有需要控制并发量。如果 getFallback() 执行速度很快,那不用修改此值。如果 getFallback() 中执行一个较为耗时的操作,那就需要考虑修改此值(或再通过另一个 HystrixCommand 调用,后续文章会详细解释)。

    五、小结

    本文介绍了 Hystrix 的基本使用方式,看完此文后,读者应该能通过使用 Hystrix 使得自己的应用具备初步的熔断降级功能。下一篇文章将介绍 Hystrix 监控相关的内容。

    六、参考

    文章

    相关文章

      网友评论

          本文标题:Hystrix 使用指南(1):基本使用

          本文链接:https://www.haomeiwen.com/subject/ppetdxtx.html