4Hystrix 线程隔离 · SpringCloud微服务实战 · 看云

4. Hystrix 线程隔离

导航

前面说了Hystrix 隔离机制中的一种信号量机制,现在我们来说一下线程隔离的机制。

4.1 线程池隔离的优缺点

本节代码地址


4.1.2 线程池的好处

  1. 应用程序会被完全保护起来,即使依赖的一个服务的线程池满了,也不会影响到应用程序的其他部分。
  2. 我们给应用程序引入一个新的风险较低的客户端lib的时候,如果发生问题,也是在本lib中,并不会影响到其他内容,因此我们可以大胆的引入新lib库。
  3. 当依赖的一个失败的服务恢复正常时,应用程序会立即恢复正常的性能。
  4. 如果我们的应用程序一些参数配置错误了,线程池的运行状况将会很快显示出来,比如延迟、超时、拒绝等。同时可以通过动态属性实时执行来处理纠正错误的参数配置。
  5. 如果服务的性能有变化,从而需要调整,比如增加或者减少超时时间,更改重试次数,就可以通过线程池指标动态属性修改,而且不会影响到其他调用请求。
  6. 除了隔离优势外,hystrix拥有专门的线程池可提供内置的并发功能,使得可以在同步调用之上构建异步的外观模式,这样就可以很方便的做异步编程(Hystrix引入了Rxjava异步框架)

注意:尽管有单独的线程提供了隔离,但您的基础客户端代码也应具有超时和/或对线程中断的响应,因此它不能无限期地阻塞并使Hystrix线程池饱和。

4.1.2 线程池的缺点

线程池的主要缺点是它们增加了计算开销。每个命令执行都涉及在单独的线程上运行命令所涉及的队列,调度和上下文切换。
Netflix在设计此系统时,决定接受此方式,以换取其提供的好处,并认为它很小,不会对成本或性能造成重大影响。

4.1.3 线程成本

Hystrix测量在子线程上执行construct()orrun()方法时的延迟以及父线程上的总的端到端时间。这样,您可以看到Hystrix开销(线程,度量,日志记录,断路器等)的成本。

Netflix API使用线程隔离每天处理10+亿次Hystrix Command执行。每个API实例有40多个线程池,每个线程池中有5-20个线程(大多数设置为10)。

下图表示一个HystrixCommand在单个API实例上以每秒60个请求的速度执行的情况(每个服务器每秒约350个线程执行总数):
7807eb13e25e72843737c35132e4566b_MD5.webp

4.2 新建FwHystrixCommondThread 类

在这里我们需要设置隔离级别为THREAD,并且为线程池的大小为3,但是在代码里面运行了6个线程,所以只可能有3个成功,剩下的回退。


@Slf4j
public class FwHystrixCommondThread extends HystrixCommand<String> {
    private final String name;

    protected FwHystrixCommondThread(String name) {
        super(Setter
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("myGroup"))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter().withExecutionIsolationStrategy(
                                HystrixCommandProperties.ExecutionIsolationStrategy.THREAD
                        )
                ).andThreadPoolPropertiesDefaults(
                        HystrixThreadPoolProperties.Setter()
                                .withCoreSize(3)
                ));
        this.name = name;
    }

    @Override
    protected String getFallback() {
        log.info(this.name+":"+Thread.currentThread().getName()+"异常");
        return this.name+":"+Thread.currentThread().getName();
    }

    @Override
    protected String run() throws Exception {
        log.info(this.name+":"+Thread.currentThread().getName()+"成功");
        return this.name + ":" + Thread.currentThread().getName();
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        for (int i = 0; i <6 ; i++) {
            final int index=i;
            Thread t =new Thread() {
                @Override
                public void run() {
                    FwHystrixCommondThread test = new FwHystrixCommondThread("test" + index);
                    test.execute();
                }
            };
            t.start();
        }
        Thread.sleep(5000);
    }
}

4.3 运行main方法

可以看到如下日志输出,跟我们预想的一样。

13:49:12.782 [Thread-0] INFO com.yisu.hystrix.without.FwHystrixCommondThread - test0:Thread-0异常
13:49:12.782 [Thread-1] INFO com.yisu.hystrix.without.FwHystrixCommondThread - test1:Thread-1异常
13:49:12.782 [Thread-2] INFO com.yisu.hystrix.without.FwHystrixCommondThread - test2:Thread-2异常
13:49:12.798 [hystrix-myGroup-2] INFO com.yisu.hystrix.without.FwHystrixCommondThread - test4:hystrix-myGroup-2成功
13:49:12.798 [hystrix-myGroup-1] INFO com.yisu.hystrix.without.FwHystrixCommondThread - test3:hystrix-myGroup-1成功
13:49:12.798 [hystrix-myGroup-3] INFO com.yisu.hystrix.without.FwHystrixCommondThread - test5:hystrix-myGroup-3成功

4.4 设置的参数

上线我设置了线程池的大小,当然其他的参数也可以设置,在这里就不介绍了,感兴趣的话自己试试,如下截图
44b704cb1a6cc1b2a9d9674e9ea10bf2_MD5.png