Hystrix高可用架构介绍

来源:互联网 发布:上海海洋大学网络教学 编辑:程序博客网 时间:2024/06/04 18:39

在复杂的分布式系统架构中,每个服务都有很多的依赖服务,而每个依赖服务都可能会故障。如果服务没有和自己的依赖服务进行隔离,那么可能某一个依赖服务的故障就会拖垮当前这个服务

Hystrix设计原则:

  1. 阻止任何一个依赖服务耗尽所有的资源,比如tomcat中的所有线程资源
  2. 避免请求排队和积压,采用限流和fail fast来控制故障
  3. 提供fallback降级机制来应对故障
  4. 使用资源隔离技术,比如bulkhead(舱壁隔离技术),swimlane(泳道技术),circuit breaker(短路技术),来限制任何一个依赖服务的故障的影响
  5. 通过近实时的统计/监控/报警功能,来提高故障发现的速度
  6. 通过近实时的属性和配置热修改功能,来提高故障处理和恢复的速度
  7. 保护依赖服务调用的所有故障情况,而不仅仅只是网络故障情况

如何实现:

(1)通过HystrixCommand或者HystrixObservableCommand来封装对外部依赖的访问请求,这个访问请求一般会运行在独立的线程中,资源隔离
具体实现代码:

  • 一次获取一条数据
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 + "!";    }}

不要因为某一个依赖服务的故障,导致耗尽了缓存服务中的所有的线程资源去执行

  • 一次获取批量数据
public class ObservableCommandHelloWorld extends HystrixObservableCommand<String> {    private final String name;    public ObservableCommandHelloWorld(String name) {        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));        this.name = name;    }    @Override    protected Observable<String> construct() {        return Observable.create(new Observable.OnSubscribe<String>() {            @Override            public void call(Subscriber<? super String> observer) {                try {                    if (!observer.isUnsubscribed()) {                    //onNext的会调用执行command时的onNext方法                        observer.onNext("Hello " + name + "!");                        observer.onNext("Hi " + name + "!");                        observer.onCompleted();                    }                } catch (Exception e) {                    observer.onError(e);                }            }         } ).subscribeOn(Schedulers.io());    }}

上述为线程池的隔离,如果使用信号量的隔离需要修改成如下:

super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()               .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)));
  • command的四种调用方式

    • 同步:
      new CommandHelloWorld(“World”).execute()
      new ObservableCommandHelloWorld(“World”).toBlocking().toFuture().get()
      如果observable command只会返回一条数据,那么可以调用上面的模式,去同步执行,返回一条数据

    • 异步:
      new CommandHelloWorld(“World”).queue()
      new ObservableCommandHelloWorld(“World”).toBlocking().toFuture()
      对command调用queue(),仅仅将command放入线程池的一个等待队列,就立即返回,拿到一个Future对象,后面可以做一些其他的事情,然后过一段时间对future调用get()方法获取数据

  • 对于ObservableCommand有两种执行方式
//这种方法会立即调用执行Observable<String> fWorld = new CommandHelloWorld("World").observe(); //会延迟调用执行,等到下面subscribe的时候才会执行//Observable<String> fWorld = new ObservableCommandHelloWorld("World").toObservable();assertEquals("Hello World!", fWorld.toBlocking().single());fWorld.subscribe(new Observer<String>() {    @Override    public void onCompleted() {    }    @Override    public void onError(Throwable e) {        e.printStackTrace();    }    @Override    public void onNext(String v) {        System.out.println("onNext: " + v);    }}); 

(2)对于超出我们设定阈值的服务调用,直接进行超时,不允许其耗费过长时间阻塞住。这个超时时间默认是99.5%的访问时间,但是一般我们可以自己设置一下
(3)为每一个依赖服务维护一个独立的线程池,或者是semaphore,当线程池已满时,直接拒绝对这个服务的调用
(4)对依赖服务的调用的成功次数,失败次数,拒绝次数,超时次数,进行统计
(5)如果对一个依赖服务的调用失败次数超过了一定的阈值,自动进行熔断,在一定时间内对该服务的调用直接降级,一段时间后再自动尝试恢复
(6)当一个服务调用出现失败,被拒绝,超时,短路等异常情况时,自动调用fallback降级机制
(7)对属性和配置的修改提供近实时的支持


核心概念:

  • 线程池和信号量隔离区别:
    对于线程池和信号量做资源隔离、限流和容量的限制,默认的容量都是10,区别在于,线程池隔离技术是用自己的线程去执行调用的,而信号量的隔离是直接让tomcat的线程去调用依赖服务的。线程池隔离适合大多数场景,例如堆外部依赖服务的网络请求和timeout等问题;而信号量隔离适合对内部一些比较复杂的业务逻辑处理。
    command threadpoll、group和key区别:

    • command key,代表了一类command,一般来说,代表了底层的依赖服务的一个接口
    • command group,代表了某一个底层的依赖服务,合理,一个依赖服务可能会暴露出来多个接口,每个接口就是一个command key;command group,在逻辑上去组织起来一堆command key的调用,统计信息,成功次数,timeout超时次数,失败次数,可以看到某一个服务整体的一些访问情况;command group,一般来说,推荐是根据一个服务去划分出一个线程池,command key默认都是属于同一个线程池的
  • 设置线程池大小:

//一般来说,用这个默认的10个线程大小就够了HystrixThreadPoolProperties.Setter()   .withCoreSize(int value)
  • 队列容量修改:
    控制queue满后reject的threshold,因为maxQueueSize不允许热修改,因此提供这个参数可以热修改,控制队列的最大大小。HystrixCommand在提交到线程池之前,其实会先进入一个队列中,这个队列满了之后,才会reject
//默认是5HystrixThreadPoolProperties.Setter()   .withQueueSizeRejectionThreshold(int value)
  • 当使用信号量隔离策略的时候,允许控制并发量来限流:
    这个并发量的设置,跟线程池大小的设置,应该是类似的,但是基于信号量的话,性能会好很多,而且hystrix框架本身的开销会小很多。默认值是10,设置的小一些,否则因为信号量是基于调用线程去执行command的,而且不能从timeout中抽离,因此一旦设置的太大,而且有延时发生,可能瞬间导致tomcat本身的线程资源本占满
//默认是10.withExecutionIsolationSemaphoreMaxConcurrentRequests(int value)
原创粉丝点击