1:Hystrix简介
1.1:分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。 对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。
这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
1.2:Hystrix是什么
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
1.3:Hystrix能干什么
Hystrix能做很多事情,主要有:
- 服务隔离、降级、熔断、限流、快速失败
- 请求合并、请求缓存
- 接近实时的监控
1.4:Hystrix的设计原则
- 防止任何单独的依赖使用所有的容器(如Tomcat)用户线程
- 切断负载并快速失败,而不是排队
- 尽可能提供回退以保护用户免受故障
- 使用隔离技术(例如舱壁,泳道和断路器模式)来限制任何一个依赖的影响
- 通过接近实时的指标,监控和警报,优化发现时间
- 通过配置更改的低延迟传播,优化恢复时间
- 防止各种客户端执行失败,而不仅仅是网络通信
2: HelloWord
加入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
官方示例https://github.com/Netflix/Hystrix/wiki/How-To-Use
3:基本使用
3.1:HystrixCommand和HystrixObservableCommand
两者主要区别是:
1:前者的命令逻辑写在run();后者的命令逻辑写在construct()
2:前者的run()是由新创建的线程执行;后者的construct()是由调用程序线程执行
3:前者一个实例只能向调用程序发送单条数据,比如上面例子中run()只能返回一个String结果;后者一个实例可以顺序发送多条数据,可以顺序调用多个onNext(),便实现了向调用程序发送多条数据
3.2:命令执行方法
execute()、queue()、observe()、toObservable()这4个方法用来触发执行run()/construct(),一个实例只能执行一次这4个方法,注意HystrixObservableCommand没有execute()和queue()。
- 1:execute():以同步堵塞方式执行run()。调用execute()后,Hystrix先创建一个新线程运行run(),接着调用程序要在execute()调用处一直堵塞着,直到run()运行完成
- 2:queue():以异步非堵塞方式执行run()。一调用queue()就直接返回一个Future对象,同时hystrix创建一个新线程运行run(),调用程序通过Future.get()拿到run()的返回结果,而Future.get()是堵塞执行的
- 3:observe():事件注册前执行run()/construct()。
第一步是事件注册前,先调用observe()自动触发执行run()/construct()(如果继承的是HystrixCommand,hystrix将创建新线程非堵塞执行run();如果继承的是HystrixObservableCommand,将以调用程序线程堵塞执行construct())
第二步是从observe()返回后调用程序调用subscribe()完成事件注册,如果run()/construct()执行成功则触发onNext()和onCompleted(),如果执行异常则触发onError() - 4:toObservable():事件注册后执行run()/construct()。
第一步是事件注册前,一调用toObservable()方法就直接返回一个Observable对象
第二步调用subscribe()完成事件注册后自动触发执行run()/construct()(如果继承的是HystrixCommand,hystrix将创建新线程非堵塞执行run(),调用程序不必等待run();如果继承的是HystrixObservableCommand,将以调用程序线程堵塞执行construct(),调用程序等待construct()执行完才能继续往下走),如果run()/construct()执行成功则触发onNext()和onCompleted(),如果执行异常则触发onError()
3.3:命令名称
默认情况下,命令名是从类名派生的:getClass().getSimpleName();
要明确地定义名字,通过HystrixCommand或HystrixObservableCommand构造函数传入,例如:
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(" HelloWorld1"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));
- 1:GroupKey 是 HystrixCommand 不可缺少的配置,其它配置均为可选,例如:
super(HystrixCommandGroupKey.Factory.asKey(“HelloWorld1”));
HystrixCommandGroupKey 的作用主要有两个:
一是起到分组监控、报警的作用;
二是在不配置 HystrixThreadPoolKey 的情况下,起到分组线程池的作用。即默认使用 HystrixCommandGroupKey 去命名线程池。使用同一个 HystrixCommandGroupKey 且没有自定义 HystrixThreadPoolKey 的 HystrixCommand 将使用同一个线程池。 - 2:commandKey:命令的标识名称
- 3:ThreadPoolKey:线程池的标识名称
3.4:Command Thread-Pool设置
虽然 HystrixCommandGroupKey 可以起到隔离线程池的作用,但是无法起到对线程池进行精细配置的作用。所以这里就需要线程池进行配置,例如:
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorld1"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("MyThreadPool"))
.andThreadPoolPropertiesDefaults(
.HystrixThreadPoolProperties.Setter()
.withCoreSize(20)
.withKeepAliveTimeMinutes(1)
.withMaxQueueSize(-1)
)
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(100)
)
- 1:andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(“MyThreadPool”)) 这是配置 ThreadPoolKey。如果需要在同一个 GroupKey 下面配置不同的 ThreadPool 就需要这个配置。
- 2:andThreadPoolPropertiesDefaults 表示设置线程池默认的属性值,包括:
(1)withCoreSize(20)
用来配置线程池大小,不配置的话使用的默认值是 10。
(2)withKeepAliveTimeMinutes(1)
用来配置核心线程数空闲时 keep alive 的时长,默认为 1 mins,一般不需要修改。
(3)withMaxQueueSize(-1)
配置线程池任务队列的大小,默认值为 -1。当使用 -1 时,SynchronousQueue将被使用,即意味着其实这个队列只是一个交换器,任务将被直接交给工作线程处理。如果工作线程不足,那任务将被拒绝;如果使用任何正整数,LinkedBlockingQueue将被使用
3.5:使用
编写完 自己的Command 之后,使用的时候每次都需要 new 一个新对象,再调用 execute() 方法。注意,不要调用 run() 方法,否则熔断、隔离等功能是不生效的
3.6:错误传播
run()里面抛出的HystrixBadRequestException只用做计数,方法抛出的所有其它异常都作为失败,触发getFallback()和断路器逻辑。
你可以包装你想要抛出的异常,HystrixBadRequestException适用的情况,如举报非法参数或非系统故障,不会计入失败的指标,不会触发回退逻辑。
3.7:快速失败
快速失败就是指没有重写getFallback,遇到异常后直接抛出,程序停止运行
3.8:回退/降级
所谓降级,就是指在在Hystrix执行非核心链路功能失败的情况下,我们如何处理,比如我们返回默认值等。触发时会调用fallback设置的降级方法,在降级方法中,可以设置默认的降级返回数据。
使用fallback机制很简单,继承HystrixCommand只需重写getFallback(),继承HystrixObservableCommand只需重写resumeWithFallback()。
如下情况将会启动回退
- 非HystrixBadRequestException异常
- run()/construct()运行超时
- 熔断器启动
- 线程池/信号量已满
3.9:请求缓存
Hystrix支持将一个请求结果缓存起来,下一个具有相同key的请求将直接从缓存中取出结果,减少请求开销。使用方式如下:
1:通过getCacheKey()在一个HystrixCommand或一个HystrixObservableCommand对象上实现该方法来启用请求缓存
2:另外要求这多个请求必须在同一个上下文。通过HystrixRequestContext.initializeContext()和context.shutdown()可以构建一个context,这两条语句间的所有请求都处于同一个context
3:通过isResponseFromCache()可检查返回结果是否来自缓存
3.10:合并请求
Hystrix支持N个请求自动合并为一个请求,这将使多次网络交互变成一次,极大节省开销。注意一点,两个请求能自动合并的前提是两者足够“近”,即两者启动执行的间隔时长要足够小,默认为10ms,即超过10ms将不自动合并。
Hystrix支持2种请求合并方式:请求范围和全局范围。这是在collapser构造中配置的,默认为request-scoped。请求范围是指在一个HystrixRequestContexts上下文中的请求;全局范围指垮HystrixRequestContexts的请求。