Hystrix是Netflix开源的一款容错框架,包含常用的容错方法:线程隔离、信号量隔离、降级策略、熔断技术。在高并发访问下,系统所依赖的服务的稳定性对系统的影响非常大,依赖有很多不可控的因素,比如网络连接变慢,资源突然繁忙,暂时不可用,服务脱机等。我们要构建稳定、可靠的分布式系统,就必须要有这样一套容错方法。
举例子:
1.
比如我们现在有3个业务调用分别是查询订单、查询商品、查询用户,且这三个业务请求都是依赖第三方服务-订单服务、商品服务、用户服务。三个服务均是通过RPC调用。当查询订单服务,假如线程阻塞了,这个时候后续有大量的查询订单请求过来,那么容器中的线程数量则会持续增加直致CPU资源耗尽到100%,整个服务对外不可用,集群环境下就是雪崩。
pom.xml的依赖
<!-- 中央库:引入hystrix等--> <repository> <id>nexus</id> <name>local private nexus</name> <url>http://search.maven.org</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> <!-- hystrix --> <hystrix.version>1.5.12</hystrix.version> <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-core</artifactId> <version>${hystrix.version}</version> </dependency>
简单例子1:
package com.book.web.test.hystrix; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; /** * 入门 * @author liweihan * */ public class CommandHelloWorld extends HystrixCommand<String> { private final String name; protected CommandHelloWorld(String name) { //最少配置:指定命令组名 super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.name = name; } @Override protected String run() throws Exception { //a real example would do work like a network call here //依赖逻辑封装在run()方法中 return "Hello " + name + "! thread: " + Thread.currentThread().getName(); } public static void main(String[] args) throws Exception{ /** * 每个Command对象只能调用一次,不可以重复调用 * 重复调用对应的异常信息:This instance can only be executed once. * Please instantiate a new instance */ CommandHelloWorld commandHelloWorld = new CommandHelloWorld("Synchronous-hystrix"); //使用execute()同步调用代码,效果等同于:commandHelloWorld.queue().get(); String s = commandHelloWorld.execute(); System.out.println(" 同步 ====== " + s); //异步调用,可以自由控制获取结果的时机 commandHelloWorld = new CommandHelloWorld("Asynchronous-hystrix"); Future<String> future = commandHelloWorld.queue(); //get()操作不能超过command定义的超时时间,默认为1秒 s = future.get(100, TimeUnit.MILLISECONDS); System.out.println(" 异步 ====== " + s); System.out.println(" 主函数 ===== " + Thread.currentThread().getName()); /** * 注意: * 异步调用使用 command.queue()get(timeout, TimeUnit.MILLISECONDS); * 同步调用使用command.execute() 等同于 command.queue().get(); */ } }
简单例子2:
package com.book.web.test.hystrix; import java.util.concurrent.TimeUnit; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixCommandProperties; import com.netflix.hystrix.HystrixThreadPoolKey; /** * 常用属性 * @author liweihan * */ public class CommandHelloWorld2 extends HystrixCommand<String>{ private final String name; protected CommandHelloWorld2(String name) { super(//命令分组用于对依赖操作分组,便于统计,汇总等. Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup")) //配置依赖超时时间,500毫秒 .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionTimeoutInMilliseconds(500)) //HystrixCommondKey工厂定义依赖名称 .andCommandKey(HystrixCommandKey.Factory.asKey("commandHelloWorld2")) //使用HystrixThreadPoolKey工厂定义线程池名称 .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool"))); this.name = name; } @Override protected String getFallback() { return "execute Falled"; } @Override protected String run() throws Exception { //sleep 1秒 ,调用会超时 TimeUnit.MILLISECONDS.sleep(100); return "Hello " + name + " thread : " + Thread.currentThread().getName(); } public static void main(String[] args) throws Exception { CommandHelloWorld2 commandHelloWorld2 = new CommandHelloWorld2("test-Fallback"); String s = commandHelloWorld2.execute(); System.out.println(" 同步 ====== " + s); /** * 注意: * 1.除了HystrixBadRequestException异常之外,所有从run()方法抛出的异常都算作失败, 并触发降级getFallback()和断路器逻辑。 2.HystrixBadRequestException用在非法参数或非系统故障异常等不应触发回退逻辑的场景。 3.每个CommandKey代表一个依赖抽象,相同的依赖要使用相同的CommandKey名称。 依赖隔离的根本就是对相同CommandKey的依赖做隔离. 4.CommandGroup是每个命令最少配置的必选参数, 在不指定ThreadPoolKey的情况下,字面值用于对不同依赖的线程池/信号区分 5.当对同一业务依赖做隔离时使用CommandGroup做区分, 但是对同一依赖的不同远程调用如(一个是redis 一个是http), 可以使用HystrixThreadPoolKey做隔离区分. 最然在业务上都是相同的组,但是需要在资源上做隔离时, 可以使用HystrixThreadPoolKey区分. 6.以下四种情况将触发getFallback调用: 1.)run()方法抛出非HystrixBadRequestException异常。 2.)run()方法调用超时 3.)熔断器开启拦截调用 4.)线程池/队列/信号量是是否跑满 实现getFallback()后,执行命令时遇到以上4中情况将被fallback接管, 不会抛出异常或其他。 */ } }
简单例子3:
package com.book.web.test.hystrix; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; /** * 请求缓存【个人不建议用,不便于管理】 * @author liweihan * */ public class CommandHelloWorld3 extends HystrixCommand<String>{ private final int id; protected CommandHelloWorld3(int id) { super(HystrixCommandGroupKey.Factory.asKey("RequestCacheCommand")); this.id = id; } @Override protected String run() throws Exception { System.out.println(Thread.currentThread().getName() + " execute id = " + id); return "execute=" + id; } //重写getCacheKey,实现区分不同请求的逻辑 @Override protected String getCacheKey() { System.out.println(" --- "); return String.valueOf(id); } public static void main(String[] args) { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { CommandHelloWorld3 commandHelloWorld3_a = new CommandHelloWorld3(22); CommandHelloWorld3 commandHelloWorld3_b = new CommandHelloWorld3(22); System.out.println("a执行结果:" + commandHelloWorld3_a.execute()); System.out.println("a执行结果是否从缓存中获取:" + commandHelloWorld3_a.isResponseFromCache); System.out.println("b执行结果:" + commandHelloWorld3_b.execute()); System.out.println("b执行结果是否从缓存中获取:" + commandHelloWorld3_b.isResponseFromCache); } finally { context.shutdown(); } context = HystrixRequestContext.initializeContext(); try { CommandHelloWorld3 commandHelloWorld3_c = new CommandHelloWorld3(22); System.out.println("c执行结果:" + commandHelloWorld3_c.execute()); System.out.println("c执行结果是否从缓存中获取:" + commandHelloWorld3_c.isResponseFromCache); } finally { context.shutdown(); } /** * 注意: * 请求缓存可以让(CommandKey/CommandGroup)相同的情况下, * 直接共享结果,降低依赖调用次数, * 在高并发和CacheKey碰撞率高场景下可以提升性能. */ } }
简单例子4:
package com.book.web.test.hystrix; import java.util.concurrent.TimeUnit; import com.netflix.hystrix.HystrixCommand; /** * 测试4-使用ApiSetter设置参数 * @author liweihan * */ public class CommandHelloWorld4 extends HystrixCommand<String>{ private Integer id; public CommandHelloWorld4(Integer id) { super(setter()); this.id = id; } private static Setter setter() { return ApiSetter.setter("getNum"); } @Override protected String run() throws Exception { TimeUnit.MINUTES.sleep(3); System.out.println(Thread.currentThread().getName() + " execute id = " + id); return "run execute=" + id; } @Override protected String getFallback() { return "getFallback --" + id; } public static void main(String[] args) { CommandHelloWorld4 commandHelloWorld4_a = new CommandHelloWorld4(2); System.out.println("a执行结果:" + commandHelloWorld4_a.execute()); } }
package com.book.web.test.hystrix; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommand.Setter; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixCommandProperties; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolProperties; /** * 调用API设置的参数或公共参数 * @author liweihan * */ public class ApiSetter { public static Setter setter(String commandKeyName,String threadPoolKeyName) { return setter("ApiGroup",commandKeyName,threadPoolKeyName); } public static Setter setter(String commandKeyName) { return setter(commandKeyName,"Api-Pool"); } /** * @author liweihan * @time 2017/12/20 16:57 * @description 相关参数设置 * @param groupKeyName 服务分组名 * @param commandKeyName 服务标识名称 * @param threadPoolKeyName 线程池名称 * @return */ public static Setter setter(String groupKeyName,String commandKeyName,String threadPoolKeyName) { //服务分组 HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey(groupKeyName); //服务标识 HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(commandKeyName); //线程池名称 HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey(threadPoolKeyName); //线程配置 HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter() .withCoreSize(25) .withKeepAliveTimeMinutes(5) .withMaxQueueSize(Integer.MAX_VALUE) .withQueueSizeRejectionThreshold(10000); //命令属性的配置 HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter() .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD) .withExecutionIsolationThreadInterruptOnTimeout(true) .withExecutionTimeoutInMilliseconds(3000) //设置超时时间为3秒时自动熔断 .withCircuitBreakerErrorThresholdPercentage(20);//失败率达到20%自动熔断 //返回 return HystrixCommand.Setter .withGroupKey(groupKey) .andCommandKey(commandKey) .andThreadPoolKey(threadPoolKey) .andThreadPoolPropertiesDefaults(threadPoolProperties) .andCommandPropertiesDefaults(commandProperties); } /** * ☆参数说明: 1.HystrixCommandGroupKey:服务分组,以上groupKey分组就包括多个服务,必填选项 2.HystrixCommandKey:服务的名称,唯一标识,如果不配置,则默认是类名 3.HystrixThreadPoolKey:线程池的名称,相同线程池名称的线程池是同一个,如果不配置,默认为分组名 4.HystrixThreadPoolProperties:线程池的配置, coreSize配置核心线程池的大小, maxQueueSize线程池队列的最大大小, queueSizeRejectionThreshold,限制当前队列的大小, 实际队列大小由这个参数决定,即到达队列里面条数到达10000,则都会被拒绝。 5.HystrixCommandProperties:配置命令的一些参数, 如executionIsolationStrategy,配置执行隔离策略,默认是使用线程隔离,THREAD即为线程池隔离, ExecutionIsolationThreadInterruptOnTimeout 使用线程隔离时,是否对命令执行超时的线程调用中断操作.默认:true 和ExecutionTimeoutInMilliseconds配置了启用超时和最大执行时间,这里为3s, circuitBreakerErrorThresholdPercentage失败率配置,默认为50%, 这里配置的为25%,即失败率到达25%触发熔断 */ }
简单例子5:
package com.book.web.test.hystrix; import java.util.concurrent.TimeUnit; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixCommandProperties; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolProperties; /** * 测试4-熔断测试 * @author liweihan * */ public class CommandHelloWorld5 extends HystrixCommand<String>{ private Integer id; public CommandHelloWorld5(Integer id) { super(setter()); this.id = id; } private static Setter setter() { return ApiSetter.setter("getNum"); } @Override protected String run() throws Exception { if (id % 2 == 0 && id <= 10) { //让小于等于10的偶数返回 return "running run():" + id; } else { //让奇数或大于10的数进入fallback TimeUnit.MILLISECONDS.sleep(200); return " XXX "; } } @Override protected String getFallback() { return " ====== CircuitBreaker fallback" + id + " ,是否进入熔断:" + super.isCircuitBreakerOpen(); } public static void main(String[] args) throws Exception { for(int i = 0; i < 30; i++) { System.out.println(new CommandHelloWorld5(i).execute()); /*Future<String> future = new CommandHelloWorld5(i).queue(); System.out.println(future.get());*/ } } private static class ApiSetter { public static Setter setter(String commandKeyName,String threadPoolKeyName) { return setter("ApiGroup",commandKeyName,threadPoolKeyName); } public static Setter setter(String commandKeyName) { return setter(commandKeyName,"Api-Pool"); } /** * @author liweihan * @time 2017/12/20 16:57 * @description 相关参数设置 * @param groupKeyName 服务分组名 * @param commandKeyName 服务标识名称 * @param threadPoolKeyName 线程池名称 * @return */ public static Setter setter(String groupKeyName,String commandKeyName,String threadPoolKeyName) { //服务分组 HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey(groupKeyName); //服务标识 HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(commandKeyName); //线程池名称 HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey(threadPoolKeyName); //线程配置 HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter() //设置核心线程池的大小,默认为10 .withCoreSize(25) //设置存活时间,单位分钟。如果coreSize小于maximumSize,那么该属性控制一个线程从实用完成到被释放的时间。默认为1 .withKeepAliveTimeMinutes(5) //设置BlockingQueue最大的队列值,默认值为-1 .withMaxQueueSize(Integer.MAX_VALUE) //设置队列拒绝的阈值——一个为设置的拒绝访问的最大队列值,即使maxQueueSize还没有达到。 //当将一个线程放入队列等待执行时,HystrixCommand使用该属性 //注意:如果maxQueueSize设置为-1,该属性不可用 ,默认为5 .withQueueSizeRejectionThreshold(10000); //命令属性的配置 HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter() .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD) //设置HystrixCommand.run()的执行是否有超时限制。默认是true .withExecutionTimeoutEnabled(true) //使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作,默认是:true //即:设置HystrixCommand.run()的执行是否在超时发生时被中断。 .withExecutionIsolationThreadInterruptOnTimeout(true) //设置调用者等待命令执行的超时限制,超过此时间,HystrixCommand被标记为TIMEOUT,并执行回退逻辑。默认是1000ms //注意:超时会作用在HystrixCommand.queue(),即使调用者没有调用get()去获得Future对象。 .withExecutionTimeoutInMilliseconds(100) //使用线程隔离时,调用超时时间,默认:1秒 ,该方法已经不建议使用! // .withExecutionIsolationThreadTimeoutInMilliseconds(3000) //失败率达到20%熔断器启动,默认值是50 .withCircuitBreakerErrorThresholdPercentage(20) // 置为true时,所有请求都将被拒绝,直接到fallback //.withCircuitBreakerForceOpen(true) //10秒内请求超过5个的话才会启动熔断器, //熔断器在设置在一个滚动窗口中,打开断路器的最少请求数。 //默认20。也就是10秒钟内至少请求20次,熔断器才发挥起作用 , //比如:如果值是20,在一个窗口内(比如10s),收到19个请求,即使这19个请求都失败了,熔断器也不会打开 //.withCircuitBreakerRequestVolumeThreshold(5) //是否启用熔断器,默认true. 启动 .withCircuitBreakerEnabled(true) //设置HystrixCommand.getCacheKey()是否启用, //由HystrixRequestCache通过请求缓存提供去重复数据功能[默认为true] .withRequestCacheEnabled(false) ; //返回 return HystrixCommand.Setter .withGroupKey(groupKey) .andCommandKey(commandKey) .andThreadPoolKey(threadPoolKey) .andThreadPoolPropertiesDefaults(threadPoolProperties) .andCommandPropertiesDefaults(commandProperties); } /** * ☆参数说明: 1.HystrixCommandGroupKey:服务分组,以上pydyn分组就包括多个服务,必填选项 2.HystrixCommandKey:服务的名称,唯一标识,如果不配置,则默认是类名 3.HystrixThreadPoolKey:线程池的名称,相同线程池名称的线程池是同一个,如果不配置,默认为分组名 4.HystrixThreadPoolProperties:线程池的配置, coreSize配置核心线程池的大小, maxQueueSize线程池队列的最大大小, queueSizeRejectionThreshold,限制当前队列的大小, 实际队列大小由这个参数决定,即到达队列里面条数到达10000,则都会被拒绝。 5.HystrixCommandProperties:配置命令的一些参数, 如executionIsolationStrategy,配置执行隔离策略,默认是使用线程隔离,THREAD即为线程池隔离, ExecutionIsolationThreadInterruptOnTimeout 使用线程隔离时,是否对命令执行超时的线程调用中断操作.默认:true 和ExecutionTimeoutInMilliseconds配置了启用超时和最大执行时间,这里为3s, circuitBreakerErrorThresholdPercentage失败率配置,默认为50%, 这里配置的为25%,即失败率到达25%触发熔断 */ } }
参考:
一、使用Netflix Hystrix编写弹性可容错的应用程序
http://blog.csdn.net/wsscy2004/article/details/50166267
二、Hystrix 使用与分析
http://blog.csdn.net/xiaoyu411502/article/details/50601687
<1.>参考
https://www.ihnbc.cn/
Hystrix使用入门手册
http://www.jianshu.com/p/b9af028efebb
使用Hystrix实现隔离
http://zhuanlan.51cto.com/art/201704/536307.htm
相关属相设置
http://www.jianshu.com/p/39763a0bd9b8
三、git地址
https://github.com/Netflix/Hystrix/wiki/Getting-Started