Hystrix是什么
分布式服务系统通常会通过HTTP或RPC方式调用所依赖的服务,例如支付服务通过HTTP或RPC调用银行卡服务。在高并发请求的情景下,依赖的服务可能会出现服务异常、网络连接缓慢、资源繁忙、暂时不可用、服务脱机等情况,这些异常情况将会严重影响整个线上系统的稳定性和可用性,最糟糕的情况是产生服务雪崩效应。复杂的分布式服务系统往往会依赖更多的其它服务,在高并发的情况下,如果没有做好隔离措施,这些依赖将会拖垮整个服务调用者。Hystrix是Netflix的一个帮助解决分布式服务系统交互时超时处理和容错的类库,它具有降级和熔断的保护能力,可以优雅的解决上述问题。
Hystrix能做什么
Hystrix提供了如下功能特性:
Hystrix如何使用
1.添加Hystrix依赖
<dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-core</artifactId> <version>1.5.12</version> </dependency>
2.使用HystrixCommand封装依赖调用
通过继承HystrixCommand封装依赖调用,示例代码如下:
public class CommandHelloWorld extends HystrixCommand<String> { private final String name; public CommandHelloWorld(String name) { super(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup")); this.name = name; } @Override protected String run() throws Exception { // 在这里调用依赖 Thread.sleep(500L); return "Hello " + name + "--" + Thread.currentThread().getId(); } // 超时、异常后执行该方法 @Override protected String getFallback() { return "fallback"; } }
run方法:run方法体中进行依赖调用。
getFallback方法:当run方法中依赖调用超时、异常(除了HystrixBadRequestException)时会执行getFallback方法快速返回;当run方法中的依赖调用在设置的时间内超时、异常(除了HystrixBadRequestException)的频率超过阈值,后续对这个依赖的调用将直接执行getFallback方法,待冷却一段时间后,对这个依赖的调用会重新进入run方法执行。
3.执行封装的依赖调用
调用execute方法即为同步执行,当前线程将一直阻塞,直到获取结果,示例代码如下:
@Test public void testSynchronous() { CommandHelloWorld commandHelloWorld = new CommandHelloWorld("jack"); System.out.print(commandHelloWorld.execute() + "--" + Thread.currentThread().getId()); }
输出结果如下:
Hello jack--16--1
从输出结果可以看到依赖调用线程和主线程不是同一个,实现了线程隔离。
HystrixCommand默认的调用超时时间是1000毫秒,如果将上述run方法中的线程休眠时间改成1100毫秒,再次运行testSynchronous单元测试,将得到如下结果:
fallback--1
可以看到在依赖调用时间超过设置的默认超时时间时,将执行getFallback方法快速返回,实现优雅降级,其过程如下图所示。
调用queue方法即为异步执行,不阻塞当前线程,返回一个Future对象,示例代码如下:
@Test public void testAsynchronous() throws Exception { CommandHelloWorld commandHelloWorld = new CommandHelloWorld("jack"); Future<String> future = commandHelloWorld.queue(); System.out.println(future.get() + "--" + Thread.currentThread().getId()); }
输出结果如下:
Hello jack--16--1
queue().get()等同于同步调用execute()
调用observe方法即为热注册观察者执行,返回一个Observable对象,当run方法执行完成后,进入观察者订阅的事件中,示例代码如下:
@Test public void testHotObservable() throws Exception { CommandHelloWorld commandHelloWorld = new CommandHelloWorld("jack"); Observable<String> ho = commandHelloWorld.observe(); //订阅结果回调事件 ho.subscribe(new Action1<String>() { public void call(String result) { //result为run方法执行返回的结果 System.out.println(result + "--" + Thread.currentThread().getId()); } }); Thread.sleep(1000); //订阅一个完整的回调事件 ho.subscribe(new Subscriber<String>() { //在onNext执行后执行 public void onCompleted() { System.out.println("oonCompleted "); } //在run/onNext方法执行异常后执行 public void onError(Throwable throwable) { } //在run方法返回结果后执行 public void onNext(String s) { System.out.println("onNext: " + s ); } }); }
输出结果如下:
Hello jack--16--16 onNext: Hello jack--16 oonCompleted
调用toObservable方法即为冷注册观察者执行,同样返回Observable对象,但它是在注册的时即执行run方法,示例代码如下:
@Test public void testColdObservable() throws Exception { CommandHelloWorld commandHelloWorld = new CommandHelloWorld("jack"); Observable<String> ho = commandHelloWorld.toObservable(); ho.subscribe(new Action1<String>() { public void call(String s) { System.out.println(s + "--" + Thread.currentThread().getId()); } }); Thread.sleep(1000); }
输出结果如下:
Hello jack--16--16
前面三种调用方式,最终都是依赖toObservable方式,这其中的转换如下图所示:
属性配置
查看HystrixCommand源码,可以发现一个常用的构造方法HystrixCommand(HystrixCommand.Setter setter),使用方法如下:
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("HelloWorld"); HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("hello"); HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("hello"); HystrixCommand.Setter setter = HystrixCommand.Setter .withGroupKey(groupKey) .andCommandKey(commandKey) .andThreadPoolKey(threadPoolKey); HystrixCommand<String> helloCommand = new HystrixCommand<String>(setter) { protected String run() throws Exception { //依赖调用 return "run"; } @Override protected String getFallback() { //fail back return super.getFallback(); } };
这个HystrixCommand.Setter中包含了如下属性:
protected final HystrixCommandGroupKey groupKey; protected HystrixCommandKey commandKey; protected HystrixThreadPoolKey threadPoolKey; protected com.netflix.hystrix.HystrixCommandProperties.Setter commandPropertiesDefaults; protected com.netflix.hystrix.HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults;
Hystrix使用单例模式存储HystrixCommand,熔断机制就是根据单实例上的调用情况统计实现的,所以每个HystrixCommand要有自己的名字,用于区分,同时用于依赖调用的隔离。HystrixCommandKey就是用于定义这个名字,如果没有定义这个名字,Hystrix会使用其类名作为其名字,可以使用HystrixCommandKey.Factory.asKey(String name)方法定义一个名称。
HystrixThreadPoolKey是HystrixCommand所在的线程池,如果该参数不设置则使用HystrixCommandGroupKey作为HystrixThreadPoolKey,这种情况下同一个HystrixCommandGroupKey下的依赖调用共用同一个线程池内,如果不想共用同一个线程池,则需要设置该参数。可以使用HystrixThreadPoolKey.Factory.asKey(String name)方法设置。
Hystrix需要对HystrixCommand进行分组,便于统计、管理,所以需要一个分组名称,HystrixCommandGroupKey就是用于定义分组名称,可以使用HystrixCommandGroupKey.Factory.asKey(String name)方法定义一个分组名。每个HystrixCommand必须要配置一个分组名,一个是用于分组,还有如果没有配置HystrixThreadPoolKey,这个分组名将会用于线程池名。
从名称上可以看出这是线程池的属性配置,可以通过它设置核心线程数大小、最大线程数、任务队列大小等,当然它也又一些默认的配置参数。
这个就是HystrixCommand的属性配置,它可以设置熔断器是否可用、熔断器熔断的错误百分比、依赖调用超时时间等,它有一些默认的配置参数,如熔断器熔断的错误百分比默认值是50%、依赖调用超时时间默认值是1000毫秒。
隔离方式
Hystrix支持线程隔离和信号量隔离:
不同的依赖调用分配到不同的线程池中执行,使用线程对依赖调用进行隔离,上述的示例代码就是使用线程隔离。优点是隔离性能好,可设置短路机制(依赖调用失败后执行getFallback()或依赖调用熔断后,一段时间内对该依赖的调用将直接返回失败),缺点是涉及到线程切换的性能损耗,但是官方给出的结果是性能损耗是可以接受的。
信号量隔离可实现对依赖调用最高并发请求数的限制,每次依赖调用都会先判断信号量是否达到阈值,如果达到极限值则拒绝调用,优点是不用新启线程,缺点是每次都需要获取信号量,使用方式如下:
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("HelloWorld"); HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("hello"); HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("hello"); //配置信号量隔离 HystrixCommandProperties.Setter commandPropertiesSetter = HystrixCommandProperties.Setter().withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE); HystrixCommand.Setter setter = HystrixCommand.Setter .withGroupKey(groupKey) .andCommandKey(commandKey) .andThreadPoolKey(threadPoolKey) .andCommandPropertiesDefaults(commandPropertiesSetter);
Hystrix工作过程
工程过程如下图所示:
步骤描述如下:
Hystrix熔断保护机制
Hystrix熔断保护就像电路中的熔断器一样,在电压过高时,保险丝会熔断,防止火灾,做到用电安全。熔断保护机制的工作过程如下图所示:
熔断器工作过程如下:
依赖调用监控
Hystrix提供了Hystrix Dashboard功能,可以实时监控依赖的调用情况。