原文地址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
註解