原⽂参考:http://hot66hot.iteye.com/blog/2155036
⼀:为什么需要Hystrix?
在⼤中型分布式系统中,通常系统很多依赖(HTTP,hession,Netty,Dubbo等),如下图:
在⾼并发访问下,这些依赖的稳定性与否对系统的影响⾮常⼤,但是依赖有很多不可控问题:如⽹络连接缓慢,资源繁忙,暂时不可⽤,服务脱机等.如下图:QPS为50的依赖 I 出现不可⽤,但是其他依赖仍然可⽤.
当依赖I 阻塞时,⼤多数服务器的线程池就出现阻塞(BLOCK),影响整个线上服务的稳定性.如下图:
在复杂的分布式架构的应⽤程序有很多的依赖,都会不可避免地在某些时候失败。⾼并发的依赖失败时如果没有隔离措施,当前应⽤服务就有被拖垮的风险。
Java代码
1. 例如:⼀个依赖30个SOA服务的系统,每个服务99.99%可⽤。 2. 99.99%的30次⽅ ≈ 99.7%
3. 0.3% 意味着⼀亿次请求 会有 3,000,00次失败 4. 换算成时间⼤约每⽉有2个⼩时服务不稳定.
5. 随着服务依赖数量的变多,服务不稳定的概率会成指数性提⾼.
解决问题⽅案:对依赖做隔离,Hystrix就是处理依赖隔离的框架,同时也是可以帮我们做依赖服务的治理和监控.
Netflix 公司开发并成功使⽤Hystrix,使⽤规模如下:
Java代码
1. The Netflix API processes 10+ billion HystrixCommand executions per day using thread isolation. 2. Each API instance has 40+ thread-pools with 5-20 threads in each (most are set to 10).
⼆:Hystrix如何解决依赖隔离
1:Hystrix使⽤命令模式HystrixCommand(Command)包装依赖调⽤逻辑,每个命令在单独线程中/信号授权下执⾏。2:可配置依赖调⽤超时时间,超时时间⼀般设为⽐99.5%平均时间略⾼即可.当调⽤超时时,直接返回或执⾏fallback逻辑。3:为每个依赖提供⼀个⼩的线程池(或信号),如果线程池已满调⽤将被⽴即拒绝,默认不采⽤排队.加速失败判定时间。
4:依赖调⽤结果分:成功,失败(抛出异常),超时,线程拒绝,短路。 请求失败(异常,拒绝,超时,短路)时执⾏fallback(降级)逻辑。5:提供熔断器组件,可以⾃动运⾏或⼿动调⽤,停⽌当前依赖⼀段时间(10秒),熔断器默认错误率阈值为50%,超过将⾃动运⾏。6:提供近实时依赖的统计和监控Hystrix依赖的隔离架构,如下图:
三:如何使⽤Hystrix
1:使⽤maven引⼊Hystrix依赖
1
2
3
5 6 11 12 13 17 18 19 21 24
View Code
2:使⽤命令模式封装依赖逻辑
1 public class HelloWorldCommand extends HystrixCommand 3 public HelloWorldCommand(String name) { 4 //最少配置:指定命令组名(CommandGroup) 5 super(HystrixCommandGroupKey.Factory.asKey(\"ExampleGroup\")); 6 this.name = name; 7 } 8 @Override 9 protected String run() { 10 // 依赖逻辑封装在run()⽅法中 11 return \"Hello \" + name +\" thread:\" + Thread.currentThread().getName();12 } 13 //调⽤实例 14 public static void main(String[] args) throws Exception{15 //每个Command对象只能调⽤⼀次,不可以重复调⽤, 16 //重复调⽤对应异常信息:This instance can only be executed once. Please instantiate a new instance.17 HelloWorldCommand helloWorldCommand = new HelloWorldCommand(\"Synchronous-hystrix\");18 //使⽤execute()同步调⽤代码,效果等同于:helloWorldCommand.queue().get(); 19 String result = helloWorldCommand.execute();20 System.out.println(\"result=\" + result);21 22 helloWorldCommand = new HelloWorldCommand(\"Asynchronous-hystrix\");23 //异步调⽤,可⾃由控制获取结果时机, 24 Future 28 System.out.println(\"mainThread=\" + Thread.currentThread().getName());29 }30 31 } 32 //运⾏结果: run()⽅法在不同的线程下执⾏ 33 // result=Hello Synchronous-hystrix thread:hystrix-HelloWorldGroup-134 // result=Hello Asynchronous-hystrix thread:hystrix-HelloWorldGroup-235 // mainThread=main View Code note:异步调⽤使⽤ command.queue()get(timeout, TimeUnit.MILLISECONDS);同步调⽤使⽤command.execute() 等同于 command.queue().get(); 3:注册异步事件回调执⾏ 1 //注册观察者事件拦截 2 Observable 4 fs.subscribe(new Action1 6 public void call(String result) { 7 //执⾏结果处理,result 为HelloWorldCommand返回的结果 8 //⽤户对结果做⼆次处理. 9 }10 }); 11 //注册完整执⾏⽣命周期事件 12 fs.subscribe(new Observer 14 public void onCompleted() { 15 // onNext/onError完成之后最后回调 16 System.out.println(\"execute onCompleted\");17 } 18 @Override 19 public void onError(Throwable e) {20 // 当产⽣异常时回调 21 System.out.println(\"onError \" + e.getMessage());22 e.printStackTrace();23 } 24 @Override 25 public void onNext(String v) {26 // 获取结果后回调 27 System.out.println(\"onNext: \" + v);28 }29 });30 /* 运⾏结果 31 call execute result=Hello observe-hystrix thread:hystrix-HelloWorldGroup-332 onNext: Hello observe-hystrix thread:hystrix-HelloWorldGroup-333 execute onCompleted34 */ View Code 4:使⽤Fallback() 提供降级策略 1 //重载HystrixCommand 的getFallback⽅法实现逻辑 2 public class HelloWorldCommand extends HystrixCommand 4 public HelloWorldCommand(String name) { 5 super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"HelloWorldGroup\")) 6 /* 配置依赖超时时间,500毫秒*/ 7 .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionIsolationThreadTimeoutInMilliseconds(500))); 8 this.name = name; 9 } 10 @Override 11 protected String getFallback() {12 return \"exeucute Falled\";13 } 14 @Override 15 protected String run() throws Exception {16 //sleep 1 秒,调⽤会超时 17 TimeUnit.MILLISECONDS.sleep(1000); 18 return \"Hello \" + name +\" thread:\" + Thread.currentThread().getName();19 } 20 public static void main(String[] args) throws Exception{ 21 HelloWorldCommand command = new HelloWorldCommand(\"test-Fallback\");22 String result = command.execute();23 }24 } 25 /* 运⾏结果:getFallback() 调⽤运⾏26 getFallback executed27 */ View Code NOTE: 除了HystrixBadRequestException异常之外,所有从run()⽅法抛出的异常都算作失败,并触发降级getFallback()和断路器逻辑。 HystrixBadRequestException⽤在⾮法参数或⾮系统故障异常等不应触发回退逻辑的场景。 5:依赖命名:CommandKey 1 public HelloWorldCommand(String name) { 2 super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"ExampleGroup\"))3 /* HystrixCommandKey⼯⼚定义依赖名称 */ 4 .andCommandKey(HystrixCommandKey.Factory.asKey(\"HelloWorld\")));5 this.name = name;6 } View Code NOTE: 每个CommandKey代表⼀个依赖抽象,相同的依赖要使⽤相同的CommandKey名称。依赖隔离的根本就是对相同CommandKey的依赖做隔离. 6:依赖分组:CommandGroup 命令分组⽤于对依赖操作分组,便于统计,汇总等. 1 //使⽤HystrixCommandGroupKey⼯⼚定义2 public HelloWorldCommand(String name) { 3 Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"HelloWorldGroup\"))4 } View Code NOTE: CommandGroup是每个命令最少配置的必选参数,在不指定ThreadPoolKey的情况下,字⾯值⽤于对不同依赖的线程池/信号区分. 7:线程池/信号:ThreadPoolKey 1 public HelloWorldCommand(String name) { 2 super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"ExampleGroup\"))3 .andCommandKey(HystrixCommandKey.Factory.asKey(\"HelloWorld\"))4 /* 使⽤HystrixThreadPoolKey⼯⼚定义线程池名称*/ 5 .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(\"HelloWorldPool\")));6 this.name = name;7 } View Code NOTE: 当对同⼀业务依赖做隔离时使⽤CommandGroup做区分,但是对同⼀依赖的不同远程调⽤如(⼀个是redis ⼀个是http),可以使⽤HystrixThreadPoolKey做隔离区分. 最然在业务上都是相同的组,但是需要在资源上做隔离时,可以使⽤HystrixThreadPoolKey区分. 8:请求缓存 Request-Cache 1 public class RequestCacheCommand extends HystrixCommand 3 public RequestCacheCommand( int id) { 4 super(HystrixCommandGroupKey.Factory.asKey(\"RequestCacheCommand\")); 5 this.id = id; 6 } 7 @Override 8 protected String run() throws Exception { 9 System.out.println(Thread.currentThread().getName() + \" execute id=\" + id);10 return \"executed=\" + id;11 } 12 //重写getCacheKey⽅法,实现区分不同请求的逻辑13 @Override 14 protected String getCacheKey() {15 return String.valueOf(id);16 }17 18 public static void main(String[] args){ 19 HystrixRequestContext context = HystrixRequestContext.initializeContext();20 try { 21 RequestCacheCommand command2a = new RequestCacheCommand(2);22 RequestCacheCommand command2b = new RequestCacheCommand(2);23 Assert.assertTrue(command2a.execute()); 24 //isResponseFromCache判定是否是在缓存中获取结果 25 Assert.assertFalse(command2a.isResponseFromCache());26 Assert.assertTrue(command2b.execute()); 27 Assert.assertTrue(command2b.isResponseFromCache());28 } finally { 29 context.shutdown();30 } 31 context = HystrixRequestContext.initializeContext();32 try { 33 RequestCacheCommand command3b = new RequestCacheCommand(2);34 Assert.assertTrue(command3b.execute()); 35 Assert.assertFalse(command3b.isResponseFromCache());36 } finally { 37 context.shutdown();38 }39 }40 } View Code NOTE:请求缓存可以让(CommandKey/CommandGroup)相同的情况下,直接共享结果,降低依赖调⽤次数,在⾼并发和CacheKey碰撞率⾼场景下可以提升性能. Servlet容器中,可以直接实⽤Filter机制Hystrix请求上下⽂ 1 public class HystrixRequestContextServletFilter implements Filter { 2 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 3 throws IOException, ServletException { 4 HystrixRequestContext context = HystrixRequestContext.initializeContext(); 5 try { 6 chain.doFilter(request, response); 7 } finally { 8 context.shutdown(); 9 }10 }11 } 12 13 15 17 18 View Code 9:信号量隔离:SEMAPHORE 隔离本地代码或可快速返回远程调⽤(如memcached,redis)可以直接使⽤信号量隔离,降低线程隔离开销. 1 public class HelloWorldCommand extends HystrixCommand 3 public HelloWorldCommand(String name) { 4 super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"HelloWorldGroup\")) 5 /* 配置信号量隔离⽅式,默认采⽤线程池隔离 */ 6 .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE))); 7 this.name = name; 8 } 9 @Override 10 protected String run() throws Exception { 11 return \"HystrixThread:\" + Thread.currentThread().getName();12 } 13 public static void main(String[] args) throws Exception{ 14 HelloWorldCommand command = new HelloWorldCommand(\"semaphore\");15 String result = command.execute();16 System.out.println(result); 17 System.out.println(\"MainThread:\" + Thread.currentThread().getName());18 }19 } 20 /** 运⾏结果 21 HystrixThread:main22 MainThread:main23 */ View Code 10:fallback降级逻辑命令嵌套 适⽤场景:⽤于fallback逻辑涉及⽹络访问的情况,如缓存访问。 1 public class CommandWithFallbackViaNetwork extends HystrixCommand 4 protected CommandWithFallbackViaNetwork(int id) { 5 super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"RemoteServiceX\")) 6 .andCommandKey(HystrixCommandKey.Factory.asKey(\"GetValueCommand\"))); 7 this.id = id; 8 } 9 10 @Override 11 protected String run() { 12 // RemoteService.getValue(id); 13 throw new RuntimeException(\"force failure for example\");14 }15 16 @Override 17 protected String getFallback() { 18 return new FallbackViaNetwork(id).execute();19 }20 21 private static class FallbackViaNetwork extends HystrixCommand 23 public FallbackViaNetwork(int id) { 24 super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"RemoteServiceX\"))25 .andCommandKey(HystrixCommandKey.Factory.asKey(\"GetValueFallbackCommand\"))26 // 使⽤不同的线程池做隔离,防⽌上层线程池跑满,影响降级逻辑. 27 .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(\"RemoteServiceXFallback\")));28 this.id = id;29 } 30 @Override 31 protected String run() { 32 MemCacheClient.getValue(id);33 }34 35 @Override 36 protected String getFallback() {37 return null;38 }39 }40 } View Code NOTE:依赖调⽤和降级调⽤使⽤不同的线程池做隔离,防⽌上层线程池跑满,影响⼆级降级逻辑调⽤. 11:显⽰调⽤fallback逻辑,⽤于特殊业务处理 1 public class CommandFacadeWithPrimarySecondary extends HystrixCommand 2 private final static DynamicBooleanProperty usePrimary = DynamicPropertyFactory.getInstance().getBooleanProperty(\"primarySecondary.usePrimaryrue); 3 private final int id; 4 public CommandFacadeWithPrimarySecondary(int id) { 5 super(Setter 6 .withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"SystemX\")) 7 .andCommandKey(HystrixCommandKey.Factory.asKey(\"PrimarySecondaryCommand\")) 8 .andCommandPropertiesDefaults( 9 HystrixCommandProperties.Setter() 10 .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)));11 this.id = id;12 } 13 @Override 14 protected String run() {15 if (usePrimary.get()) { 16 return new PrimaryCommand(id).execute();17 } else { 18 return new SecondaryCommand(id).execute();19 }20 } 21 @Override 22 protected String getFallback() {23 return \"static-fallback-\" + id;24 } 25 @Override 26 protected String getCacheKey() {27 return String.valueOf(id);28 } 29 private static class PrimaryCommand extends HystrixCommand 31 private PrimaryCommand(int id) {32 super(Setter 33 .withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"SystemX\")) 34 .andCommandKey(HystrixCommandKey.Factory.asKey(\"PrimaryCommand\"))35 .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(\"PrimaryCommand\"))36 .andCommandPropertiesDefaults( 37 // we default to a 600ms timeout for primary 38 HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(600)));39 this.id = id;40 } 41 @Override 42 protected String run() { 43 // perform expensive 'primary' service call44 return \"responseFromPrimary-\" + id;45 }46 } 47 private static class SecondaryCommand extends HystrixCommand 49 private SecondaryCommand(int id) {50 super(Setter 51 .withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"SystemX\")) 52 .andCommandKey(HystrixCommandKey.Factory.asKey(\"SecondaryCommand\"))53 .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(\"SecondaryCommand\"))54 .andCommandPropertiesDefaults( 55 // we default to a 100ms timeout for secondary 56 HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100)));57 this.id = id;58 } 59 @Override 60 protected String run() { 61 // perform fast 'secondary' service call62 return \"responseFromSecondary-\" + id;63 }64 } 65 public static class UnitTest {66 @Test 67 public void testPrimary() { 68 HystrixRequestContext context = HystrixRequestContext.initializeContext();69 try { 70 ConfigurationManager.getConfigInstance().setProperty(\"primarySecondary.usePrimaryrue); 71 assertEquals(\"responseFromPrimary-20\new CommandFacadeWithPrimarySecondary(20).execute());72 } finally { 73 context.shutdown(); 74 ConfigurationManager.getConfigInstance().clear();75 }76 } 77 @Test 78 public void testSecondary() { 79 HystrixRequestContext context = HystrixRequestContext.initializeContext();80 try { 81 ConfigurationManager.getConfigInstance().setProperty(\"primarySecondary.usePrimary\false); 82 assertEquals(\"responseFromSecondary-20\new CommandFacadeWithPrimarySecondary(20).execute());83 } finally { 84 context.shutdown(); 85 ConfigurationManager.getConfigInstance().clear();86 }87 }88 }89 } View Code NOTE:显⽰调⽤降级适⽤于特殊需求的场景,fallback⽤于业务处理,fallback不再承担降级职责,建议慎重使⽤,会造成监控统计换乱等问题. 12:命令调⽤合并:HystrixCollapser 命令调⽤合并允许多个请求合并到⼀个线程/信号下批量执⾏。执⾏流程图如下: 1 public class CommandCollapserGetValueForKey extends HystrixCollapser 3 public CommandCollapserGetValueForKey(Integer key) { 4 this.key = key; 5 } 6 @Override 7 public Integer getRequestArgument() { 8 return key; 9 } 10 @Override 11 protected HystrixCommand 13 return new BatchCommand(requests);14 } 15 @Override 16 protected void mapResponseToRequests(List 18 for (CollapsedRequest 20 request.setResponse(batchResponse.get(count++));21 }22 } 23 private static final class BatchCommand extends HystrixCommand 25 private BatchCommand(Collection 26 super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(\"ExampleGroup\"))27 .andCommandKey(HystrixCommandKey.Factory.asKey(\"GetValueForKey\")));28 this.requests = requests;29 } 30 @Override 31 protected List 32 ArrayList 33 for (CollapsedRequest 36 return response;37 }38 } 39 public static class UnitTest { 40 HystrixRequestContext context = HystrixRequestContext.initializeContext();41 try { 42 Future 50 assertEquals(1, HystrixRequestLog.getCurrentRequest().getExecutedCommands().size()); 51 HystrixCommand> command = HystrixRequestLog.getCurrentRequest().getExecutedCommands().toArray(new HystrixCommand>[1])[0];52 assertEquals(\"GetValueForKey\ 53 assertTrue(command.getExecutionEvents().contains(HystrixEventType.COLLAPSED));54 assertTrue(command.getExecutionEvents().contains(HystrixEventType.SUCCESS));55 } finally { 56 context.shutdown();57 } 58 }59 } View Code NOTE:使⽤场景:HystrixCollapser⽤于对多个相同业务的请求合并到⼀个线程甚⾄可以合并到⼀个连接中执⾏,降低线程交互次和IO数,但必须保证他们属于同⼀依赖. 四:监控平台搭建Hystrix-dashboard 1:监控dashboard介绍 dashboard⾯板可以对依赖关键指标提供实时监控,如下图: 2:实例暴露command统计数据 Hystrix使⽤Servlet对当前JVM下所有command调⽤情况作数据流输出配置如下: 1 2 4 6 7 View Code 3:集群模式监控统计搭建 1)使⽤Turbine组件做集群数据汇总结构图如下; 2)内嵌jetty提供Servlet容器,暴露HystrixMetrics 1 public class JettyServer { 2 private final Logger logger = LoggerFactory.getLogger(this.getClass()); 3 private int port; 4 private ExecutorService executorService = Executors.newFixedThreadPool(1); 5 private Server server = null; 6 public void init() { 7 try { 8 executorService.execute(new Runnable() { 9 @Override 10 public void run() {11 try { 12 //绑定8080端⼝,加载HystrixMetricsStreamServlet并映射url13 server = new Server(8080); 14 WebAppContext context = new WebAppContext();15 context.setContextPath(\"/\"); 16 context.addServlet(HystrixMetricsStreamServlet.class, \"/hystrix.stream\");17 context.setResourceBase(\".\");18 server.setHandler(context);19 server.start();20 server.join(); 21 } catch (Exception e) { 22 logger.error(e.getMessage(), e);23 }24 }25 }); 26 } catch (Exception e) { 27 logger.error(e.getMessage(), e);28 }29 } 30 public void destory() {31 if (server != null) {32 try { 33 server.stop(); 34 server.destroy(); 35 logger.warn(\"jettyServer stop and destroy!\");36 } catch (Exception e) { 37 logger.error(e.getMessage(), e);38 }39 }40 }41 } View Code 3)Turbine搭建和配置 a:配置Turbine Servlet收集器 1 2 3 5 7 8 View Code b:编写config.properties配置集群实例 #配置两个集群:mobil-online,ugc-online turbine.aggregator.clusterConfig=mobil-online,ugc-online#配置mobil-online集群实例 turbine.ConfigPropertyBasedDiscovery.mobil-online.instances=10.10.*.*,10.10.*.*,10.10.*.*,10.10.*.*,10.10.*.*,10.10.*.*,10.16.*.*,10.16.*.*,10.16.*.*,10.16.*.*#配置mobil-online数据流servlet turbine.instanceUrlSuffix.mobil-online=:8080/hystrix.stream#配置ugc-online集群实例 turbine.ConfigPropertyBasedDiscovery.ugc-online.instances=10.10.*.*,10.10.*.*,10.10.*.*,10.10.*.*#配置ugc-online数据流servletturbine.instanceUrlSuffix.ugc-online=:8080/hystrix.stream View Code c:使⽤Dashboard配置连接Turbine 如下图 : 五:Hystrix配置与分析 1:Hystrix 配置 1):Command 配置 Command配置源码在HystrixCommandProperties,构造Command时通过Setter进⾏配置具体配置解释和默认值如下 1 //使⽤命令调⽤隔离⽅式,默认:采⽤线程隔离,ExecutionIsolationStrategy.THREAD 2 private final HystrixProperty 4 private final HystrixProperty 6 private final HystrixProperty 8 private final HystrixProperty 10 private final HystrixProperty 12 private final HystrixProperty 13 // 使⽤线程隔离时,是否对命令执⾏超时的线程调⽤中断(Thread.interrupt())操作.默认:true14 private final HystrixProperty 16 private final HystrixProperty 18 private final HystrixProperty 20 private final HystrixProperty 22 private final HystrixProperty 24 private final HystrixProperty View Code 2):熔断器(Circuit Breaker)配置 Circuit Breaker配置源码在HystrixCommandProperties,构造Command时通过Setter进⾏配置,每种依赖使⽤⼀个Circuit Breaker 1 // 熔断器在整个统计时间内是否开启的阀值,默认20秒。也就是10秒钟内⾄少请求20次,熔断器才发挥起作⽤ 2 private final HystrixProperty 3 //熔断器默认⼯作时间,默认:5秒.熔断器中断请求5秒后会进⼊半打开状态,放部分流量过去重试 4 private final HystrixProperty 6 private final HystrixProperty 8 private final HystrixProperty 10 private final HystrixProperty 12 private final HystrixProperty View Code 3):命令合并(Collapser)配置 Command配置源码在HystrixCollapserProperties,构造Collapser时通过Setter进⾏配置 1 //请求合并是允许的最⼤请求数,默认: Integer.MAX_VALUE 2 private final HystrixProperty 4 private final HystrixProperty 6 private final HystrixProperty View Code 4):线程池(ThreadPool)配置 1 /** 2 配置线程池⼤⼩,默认值10个. 3 建议值:请求⾼峰时99.5%的平均响应时间 + 向上预留⼀些即可 4 */ 5 HystrixThreadPoolProperties.Setter().withCoreSize(int value) 6 /** 7 配置线程值等待队列长度,默认值:-1 8 建议值:-1表⽰不等待直接拒绝,测试表明线程池使⽤直接决绝策略+ 合适⼤⼩的⾮回缩线程池效率最⾼.所以不建议修改此值。 9 当使⽤⾮回缩线程池时,queueSizeRejectionThreshold,keepAliveTimeMinutes 参数⽆效 10 */ 11 HystrixThreadPoolProperties.Setter().withMaxQueueSize(int value) View Code 2:Hystrix关键组件分析 1):Hystrix流程结构解析 流程说明: 1:每次调⽤创建⼀个新的HystrixCommand,把依赖调⽤封装在run()⽅法中.2:执⾏execute()/queue做同步或异步调⽤. 3:判断熔断器(circuit-breaker)是否打开,如果打开跳到步骤8,进⾏降级策略,如果关闭进⼊步骤.4:判断线程池/队列/信号量是否跑满,如果跑满进⼊降级步骤8,否则继续后续步骤.5:调⽤HystrixCommand的run⽅法.运⾏依赖逻辑5a:依赖逻辑调⽤超时,进⼊步骤8.6:判断逻辑是否调⽤成功6a:返回成功调⽤结果6b:调⽤出错,进⼊步骤8. 7:计算熔断器状态,所有的运⾏状态(成功, 失败, 拒绝,超时)上报给熔断器,⽤于统计从⽽判断熔断器状态.8:getFallback()降级逻辑. 以下四种情况将触发getFallback调⽤: (1):run()⽅法抛出⾮HystrixBadRequestException异常。 (2):run()⽅法调⽤超时 (3):熔断器开启拦截调⽤ (4):线程池/队列/信号量是否跑满 8a:没有实现getFallback的Command将直接抛出异常8b:fallback降级逻辑调⽤成功直接返回8c:降级逻辑调⽤失败抛出异常9:返回执⾏成功结果2):熔断器:Circuit Breaker Circuit Breaker 流程架构和统计 每个熔断器默认维护10个bucket,每秒⼀个bucket,每个blucket记录成功,失败,超时,拒绝的状态,默认错误超过50%且10秒内超过20个请求进⾏中断拦截. 3)隔离(Isolation)分析 Hystrix隔离⽅式采⽤线程/信号的⽅式,通过隔离限制依赖的并发量和阻塞扩散. (1):线程隔离 把执⾏依赖代码的线程与请求线程(如:jetty线程)分离,请求线程可以⾃由控制离开的时间(异步过程)。 通过线程池⼤⼩可以控制并发量,当线程池饱和时可以提前拒绝服务,防⽌依赖问题扩散。 线上建议线程池不要设置过⼤,否则⼤量堵塞线程有可能会拖慢服务器。 (2):线程隔离的优缺点 线程隔离的优点: [1]:使⽤线程可以完全隔离第三⽅代码,请求线程可以快速放回。 [2]:当⼀个失败的依赖再次变成可⽤时,线程池将清理,并⽴即恢复可⽤,⽽不是⼀个长时间的恢复。[3]:可以完全模拟异步调⽤,⽅便异步编程。线程隔离的缺点: [1]:线程池的主要缺点是它增加了cpu,因为每个命令的执⾏涉及到排队(默认使⽤SynchronousQueue避免排队),调度和上下⽂切换。[2]:对使⽤ThreadLocal等依赖线程状态的代码增加复杂性,需要⼿动传递和清理线程状态。NOTE: Netflix公司内部认为线程隔离开销⾜够⼩,不会造成重⼤的成本或性能的影响。 Netflix 内部API 每天100亿的HystrixCommand依赖请求使⽤线程隔,每个应⽤⼤约40多个线程池,每个线程池⼤约5-20个线程。 (3):信号隔离 信号隔离也可以⽤于限制并发访问,防⽌阻塞扩散, 与线程隔离最⼤不同在于执⾏依赖代码的线程依然是请求线程(该线程需要通过信号申请), 如果客户端是可信的且可以快速返回,可以使⽤信号隔离替换线程隔离,降低开销. 线程隔离与信号隔离区别如下图: 解析图⽚出⾃官⽹wiki , 更多内容请见官⽹: 因篇幅问题不能全部显示,请点此查看更多更全内容, String, Integer> { 2 private final Integer key;
> createCommand(final Collection
> {24 private final Collection