Skip to content

Hystrix

Calvin Xiao edited this page Nov 15, 2013 · 43 revisions

功能

NetFlix的Hystrix项目 提供对远程资源/服务的访问的最具有通用性的保护方式。

  • 提供Fail Fast的短路保护机制(时间段内频繁出错则进入保护期,不再访问资源而直接拒绝请求)
  • 提供并发控制
  • 在独立线程模式下提供超时控制
  • 记录访问延时等Metrics数据,为入口限流或自身并发控制的动态调整提供支持
  • 为包括短路在内的各种出错情况,提供抛异常以外的备选方案,如总是返回true或empty list

Hystrix运行原理

How it works的Flow Chart一节,很好的讲述了整个保护的流程。

  1. 如果在窗口时间(默认20秒)内,出错比例达到多少(默认50%)就进入短路状态(当然访问次数要达到某个最小值,默认20,不能说窗口内第1次,如果失败了就是100%失败)
  2. 短路状态下,所有访问都会立即返回,不会真正访问远程资源。保护期(默认5秒)过了之后,再尝试访问一次,如果依然错误,则继续保护状态。
  3. 如果是独立线程池模式,超时默认为1秒,超时立刻返回异常并算入异常计数。同时池大小默认为10,如果满了,同样立刻返回错误并记入异常计数。如果依然使用调用者线程来执行Command,使用信号量控制并发,同样默认为10。

因为采用了Command模式,所有对远程资源的访问都放入到T run()函数中,因此具有很强的普适性,并绑定任何框架,Hystrix自己只专注于保护的计算,这是它定位清晰聪明的地方。

SpringSide中的演示

配置

首先,Hystrix不支持Annotation式的配置。其次,在Hystrix自己的演示中,是在Command的构造函数里进行配置。实际上Command的配置可能需要从某些配置源读取,所以在SpringSide里,在Service层构建可重用的配置并在创建Command时传入。

异常控制

异常有两种,一种是远端服务的异常,一种是正常的异常。

正常的异常比如输入参数的校验错误,400 Bad Request,又或者其他的正常的业务异常,比如客户已欠费。此时,run函数里应该主动捕捉这些异常,用HystrixBadRequestException包裹并重新抛出,此时异常不算入短路计算内。

throw new HystrixBadRequestException(e.getMessage(), e);

Hystrix的HystrixRuntimeException异常,包括了run()内抛出异常、与Hystrix因为短路保护、超时、并发过大而抛出的各种错误,e.getFailureType()可以得到错误类型。如果是FailureType.COMMAND_EXCEPTION,即run()内抛出异常,还可以用e.getCause()得出真正的原因。

在SpringSide中,并没有在Service层进行错误的翻译,而是一直留到Web层的HystrixExceptionHandler进行统一处理。

另外,Command还提供了Fallback接口,对各种错误提供一个备选处理方案,如果没有实现则直接抛出异常。在实现里可以永远返回true,返回DummyUser,或者访问备用节点等。遗憾是此函数并没有提供具体的错误背景信息,所以也无法细致的处理异常。

Metrics

在Service层的getHystrixMetrics()函数,演示了各种对外报告的Metrics,比较有趣的是延时报告,可以报告最快的50%请求的平均延时,95%的延时等,剔除极端情况的影响。

另外Hystrix还能与Netflix Servo集成

另Hystrix还提供一个DashBoard页面,需要部署一个dashboard的war包,如果监控多个服务器还要安装用于聚合数据的turbine项目的war包。

FAQ

为什么要用有侵入的Command模式,而不是AOP

参见FAQ里的两个问题,作者觉得访问外部应用是件严肃的事情,值得认真的写成Command模式:

要不要用新开独立线程池模式

因为如果不是在新开的线程里运行,则无法在设定超时(比如2秒)时断开请求,只能依靠远程访问者自己的超时机制,如HttpClient并设定15秒超时。据Hystrix自己测试,使用新线程的性能差异有一点,但不很大 Cost of Threads。 而并发控制,用了线程池就用池大小,如果还是用原来的线程,就用一个信号量。