原文地址1java
原文地址2git
全文概览github
[TOC]面试
hystrix官网地址githubspring
Hystrix是一个低延迟和容错的第三方组件库。旨在隔离远程系统、服务和第三方库的访问点。官网上已经中止维护并推荐使用resilience4j。可是国内的话咱们有springcloud alibaba。express
Hystrix 经过隔离服务之间的访问来实现分布式系统中延迟及容错机制来解决服务雪崩场景而且基于hystrix能够提供备选方案(fallback)。缓存
$$ 99.99^{30} = 99.7\% \quad uptime \\ 0.3\% \quad of \quad 1 \quad billion \quad requests \quad = \quad 3,000,000 \quad failures \\ 2+ \quad hours \quad downtime/month \quad even \quad if \quad all \quad dependencies \quad have \quad excellent \quad uptime. $$tomcat
虽然包里面包含了hystrix 。咱们仍是引入对应的start开启相关配置吧。这里其实就是在openfeign专题中的列子。在那个专题咱们提供了PaymentServiceFallbackImpl、PaymentServiceFallbackFactoryImpl两个类做为备选方案。不过当时咱们只需指出openfeign支持设置两种方式的备选方案。今天咱们网络
<!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
演示下传统企业没有备选方案的状况会发生什么灾难。并发
首先咱们对payment#createByOrder接口进行测试。查看下响应状况
在测试payment#getTimeout/id方法。
还记得咱们上面为了演示并发将order模块的最大线程数设置为10.这里咱们经过测试工具调用下order/getpayment/1这个接口看看日志打印状况
@HystrixCommand( groupKey = "order-service-getPaymentInfo", commandKey = "getPaymentInfo", threadPoolKey = "orderServicePaymentInfo", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000") }, threadPoolProperties = { @HystrixProperty(name = "coreSize" ,value = "6"), @HystrixProperty(name = "maxQueueSize",value = "100"), @HystrixProperty(name = "keepAliveTimeMinutes",value = "2"), @HystrixProperty(name = "queueSizeRejectionThreshold",value = "100") }, fallbackMethod = "getPaymentInfoFallback" ) @RequestMapping(value = "/getpayment/{id}",method = RequestMethod.GET) public ResultInfo getPaymentInfo(@PathVariable("id") Long id) { log.info(Thread.currentThread().getName()); return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, ResultInfo.class); } public ResultInfo getPaymentInfoFallback(@PathVariable("id") Long id) { log.info("已经进入备选方案了,下面交由自由线程执行"+Thread.currentThread().getName()); return new ResultInfo(); } @HystrixCommand( groupKey = "order-service-getpaymentTimeout", commandKey = "getpaymentTimeout", threadPoolKey = "orderServicegetpaymentTimeout", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "10000") }, threadPoolProperties = { @HystrixProperty(name = "coreSize" ,value = "3"), @HystrixProperty(name = "maxQueueSize",value = "100"), @HystrixProperty(name = "keepAliveTimeMinutes",value = "2"), @HystrixProperty(name = "queueSizeRejectionThreshold",value = "100") } ) @RequestMapping(value = "/getpaymentTimeout/{id}",method = RequestMethod.GET) public ResultInfo getpaymentTimeout(@PathVariable("id") Long id) { log.info(Thread.currentThread().getName()); return orderPaymentService.getTimeOut(id); }
并发量在getpaymentTimeout | getpaymentTimeout/{id} | /getpayment/{id} |
---|---|---|
20 | 三个线程打满后一段时间开始报错 | 能够正常响应;也会慢,cpu线程切换须要时间 |
30 | 同上 | 同上 |
50 | 同上 | 也会超时,由于order调用payment服务压力会受影响 |
@HystrixCommand( commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000"), @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY,value = "SEMAPHORE"), @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS,value = "6") }, fallbackMethod = "getPaymentInfoFallback" )
措施 | 优势 | 缺点 | 超时 | 熔断 | 异步 |
---|---|---|---|---|---|
线程隔离 | 一个调用一个线程池;互相不干扰;保证高可用 | cpu线程切换开销 | √ | √ | √ |
信号量隔离 | 避免CPU切换。高效 | 在高并发场景下须要存储信号量变大 | × | √ | × |
还记的上面咱们order50个并发的timeout的时候会致使getpayment接口异常,当时定位了是由于原生payment服务压力撑不住致使的。若是咱们在payment上加入fallback就能保证在资源不足的时候也能快速响应。这样至少能保证order#getpayment方法的可用性。
hystrix除了在方法上特殊定制的fallback之外,还有一个全局的fallback。只须要在类上经过@DefaultProperties(defaultFallback = "globalFallback")
来实现全局的备选方案。一个方法知足触发降级的条件时若是该请求对应的HystrixCommand
注解中没有配置fallback则使用所在类的全局fallback。若是全局也没有则抛出异常。
DefaultProperties
能够避免每一个接口都配置fallback。可是这种的全局好像还不是全局的fallback。咱们仍是须要每一个类上配置fallback。笔者查阅了资料好像也没有FallbackFactory
这个类吗。这个类能够理解成spring的BeanFactory
。这个类是用来产生咱们所须要的FallBack
的。咱们在这个工厂里能够生成一个通用类型的fallback的代理对象。代理对象能够根据代理方法的方法签名进行入参和出参。FallBackFactory
感兴趣的能够下载源码查看或者进主页查看openfeign专题。@HystrixCommand( commandProperties = { @HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间范围 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸 }, fallbackMethod = "getInfoFallback" ) @RequestMapping(value = "/get", method = RequestMethod.GET) public ResultInfo get(@RequestParam Long id) { if (id < 0) { int i = 1 / 0; } log.info(Thread.currentThread().getName()); return orderPaymentService.get(id); } public ResultInfo getInfoFallback(@RequestParam Long id) { return new ResultInfo(); }
circuitBreaker.requestVolumeThreshold
设置统计请求次数circuitBreaker.sleepWindowInMilliseconds
设置时间滑动单位 , 在触发熔断后多久进行尝试开放,及俗称的半开状态circuitBreaker.errorThresholdPercentage
设置触发熔断开关的临界条件http://localhost/order/get?id=-1
进行20次测试。虽然这20次无一例额外都会报错。可是咱们会发现一开始报错是由于咱们代码里的错误。后面的错误就是hystrix熔断的错误了。一开始试by zero 错误、后面就是short-circuited and fallback failed 熔断错误了order/getId?id=1
忽然有一万个请求过来。为了缓解压力咱们集中一下请求每100个请求调用一次order/getIds?ids=xxxxx
。这样咱们最终到payment模块则是10000/100=100个请求。下面咱们经过代码配置实现下请求合并。@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface HystrixCollapser { String collapserKey() default ""; String batchMethod(); Scope scope() default Scope.REQUEST; HystrixProperty[] collapserProperties() default {}; }
属性 | 含义 |
---|---|
collapserKey | 惟一标识 |
batchMethod | 请求合并处理方法。即合并后须要调用的方法 |
scope | 做用域;两种方式[REQUEST, GLOBAL] ; REQUEST : 在同一个用户请求中达到条件将会合并 GLOBAL : 任何线程的请求都会加入到这个全局统计中 |
HystrixProperty[] | 配置相关参数 |
@HystrixCollapser( scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, batchMethod = "getIds", collapserProperties = { @HystrixProperty(name = HystrixPropertiesManager.MAX_REQUESTS_IN_BATCH , value = "3"), @HystrixProperty(name = HystrixPropertiesManager.TIMER_DELAY_IN_MILLISECONDS, value = "10") } ) @RequestMapping(value = "/getId", method = RequestMethod.GET) public ResultInfo getId(@RequestParam Long id) { if (id < 0) { int i = 1 / 0; } log.info(Thread.currentThread().getName()); return null; } @HystrixCommand public List<ResultInfo> getIds(List<Long> ids) { System.out.println(ids.size()+"@@@@@@@@@"); return orderPaymentService.getIds(ids); }
①、建立HystrixCommand或者HystrixObservableCommand对象
方法 | 做用 |
---|---|
execute | 同步执行;返回结果对象或者异常抛出 |
queue | 异步执行;返回Future对象 |
observe | 返回Observable对象 |
toObservable | 返回Observable对象 |
hystrix-dashboard
两个模块就好了<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
EnableHystrixDashboard
就引入了dashboard了。 咱们不须要进行任何开发。这个和eureka同样主须要简单的引包就能够了。@Component public class HystrixConfig { @Bean public ServletRegistrationBean getServlet(){ HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); //注意这里配置的/hystrix.stream 最终访问地址就是 localhost:port/hystrix.stream ; 若是在配置文件中配置在新版本中是须要 //加上actuator 即 localhost:port/actuator registrationBean.addUrlMappings("/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; } }
localhost/hystrix.stream
就会出现ping的界面。表示咱们order模块安装监控成功。固然order也须要actuator模块hystrix-dashboard
来对咱们的order模块进行监控。可是实际应用中咱们不可能只在order中配置hystrix的。<!--新增hystrix dashboard--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-turbine</artifactId> </dependency>
spring: application: name: cloud-hystrix-turbine eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:7001/eureka instance: prefer-ip-address: true # 聚合监控 turbine: app-config: cloud-order-service,cloud-payment-service cluster-name-expression: "'default'" # 该处配置和url同样。若是/actuator/hystrix.stream 的则须要配置actuator instanceUrlSuffix: hystrix.stream
启动类上添加EnableTurbine
注解