全文概覽
[TOC]
為什么需要hystrix
Hystrix同樣是netfix公司在分布式系統(tǒng)中的貢獻(xiàn)。同樣的也進(jìn)入的不維護(hù)階段。不維護(hù)不代表被淘汰。只能說(shuō)明推陳出新技術(shù)在不斷迭代。曾今的輝煌曾經(jīng)的設(shè)計(jì)還是值得我們?nèi)W(xué)習(xí)的。
在分布式環(huán)境中,服務(wù)調(diào)度是特色也是頭疼的一塊。在服務(wù)治理章節(jié)我們介紹了服務(wù)治理的功能。前一課我們也介紹了ribbon、feign進(jìn)行服務(wù)調(diào)用?,F(xiàn)在自然的到了服務(wù)監(jiān)控管理了。hystrix就是對(duì)服務(wù)進(jìn)行隔離保護(hù)。以實(shí)現(xiàn)服務(wù)不會(huì)出現(xiàn)連帶故障。導(dǎo)致整個(gè)系統(tǒng)不可用

- 如上圖所示,當(dāng)多個(gè)客戶端進(jìn)行服務(wù)調(diào)用Aservice時(shí),而在分布式系統(tǒng)中Aservice存在三臺(tái)服務(wù),其中Aservice某些邏輯需要Bservice處理。Bservice在分布式系統(tǒng)中部署了兩臺(tái)服務(wù)。這個(gè)時(shí)候因?yàn)榫W(wǎng)絡(luò)問(wèn)題導(dǎo)致Aservice中有一臺(tái)和Bservice的通信異常。如果Bservice是做日志處理的。在整個(gè)系統(tǒng)看來(lái)日志丟了和系統(tǒng)宕機(jī)比起來(lái)應(yīng)該無(wú)所謂了。但是這個(gè)時(shí)候因?yàn)榫W(wǎng)絡(luò)通信問(wèn)題導(dǎo)致Aservice整個(gè)服務(wù)不可用了。有點(diǎn)得不嘗試。

- 在看上圖 。 A-->B-->C-->D 。此時(shí)D服務(wù)宕機(jī)了。C因?yàn)镈宕機(jī)出現(xiàn)處理異常。但是C的線程卻還在為B響應(yīng)。這樣隨著并發(fā)請(qǐng)求進(jìn)來(lái)時(shí),C服務(wù)線程池出現(xiàn)爆滿導(dǎo)致CPU上漲。在這個(gè)時(shí)候C服務(wù)的其他業(yè)務(wù)也會(huì)受到CPU上漲的影響導(dǎo)致響應(yīng)變慢。
特色功能
Hystrix是一個(gè)低延遲和容錯(cuò)的第三方組件庫(kù)。旨在隔離遠(yuǎn)程系統(tǒng)、服務(wù)和第三方庫(kù)的訪問(wèn)點(diǎn)。官網(wǎng)上已經(jīng)停止維護(hù)并推薦使用resilience4j。但是國(guó)內(nèi)的話我們有springcloud alibaba。
Hystrix 通過(guò)隔離服務(wù)之間的訪問(wèn)來(lái)實(shí)現(xiàn)分布式系統(tǒng)中延遲及容錯(cuò)機(jī)制來(lái)解決服務(wù)雪崩場(chǎng)景并且基于hystrix可以提供備選方案(fallback)。
- 對(duì)網(wǎng)絡(luò)延遲及故障進(jìn)行容錯(cuò)
- 阻斷分布式系統(tǒng)雪崩
- 快速失敗并平緩恢復(fù)
- 服務(wù)降級(jí)
- 實(shí)時(shí)監(jiān)控、警報(bào)
- 上面試官網(wǎng)給出的一個(gè)統(tǒng)計(jì)。在30臺(tái)服務(wù)中每臺(tái)出現(xiàn)異常的概覽是0.01%。一億個(gè)請(qǐng)求就會(huì)有300000失敗。這樣換算下每個(gè)月至少有2小時(shí)停機(jī)。這對(duì)于互聯(lián)網(wǎng)系統(tǒng)來(lái)說(shuō)是致命的。

- 上圖是官網(wǎng)給出的兩種情況。和我們上章節(jié)的類似。都是介紹服務(wù)雪崩的場(chǎng)景。
項(xiàng)目準(zhǔn)備
- 在openfeign專題中我們就探討了基于feign實(shí)現(xiàn)的服務(wù)熔斷當(dāng)時(shí)說(shuō)了內(nèi)部就是基于hystrix。當(dāng)時(shí)我們也看了pom內(nèi)部的結(jié)構(gòu)在eureka中內(nèi)置ribbon的同時(shí)也內(nèi)置了hystrix模塊。

-
雖然包里面包含了hystrix 。我們還是引入對(duì)應(yīng)的start開啟相關(guān)配置吧。這里其實(shí)就是在openfeign專題中的列子。在那個(gè)專題我們提供了PaymentServiceFallbackImpl、PaymentServiceFallbackFactoryImpl兩個(gè)類作為備選方案。不過(guò)當(dāng)時(shí)我們只需指出openfeign支持設(shè)置兩種方式的備選方案。今天我們
<!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>演示下傳統(tǒng)企業(yè)沒有備選方案的情況會(huì)發(fā)生什么災(zāi)難。


接口測(cè)試
-
首先我們對(duì)payment#createByOrder接口進(jìn)行測(cè)試。查看下響應(yīng)情況
image-20210415103254641 -
在測(cè)試payment#getTimeout/id方法。
image-20210415103254641- 現(xiàn)在我們用jemeter來(lái)壓測(cè)payment#getTimeOut/id這個(gè)接口。一位需要4S等待會(huì)照成資源消耗殆盡問(wèn)題。這個(gè)時(shí)候我們的payment#createByOrder也會(huì)被阻塞。
image-20210415103254641- spring中默認(rèn)的tomcat的最大線程數(shù)是200.為了保護(hù)我們辛苦的筆記本。這里我們將線程數(shù)設(shè)置小點(diǎn)。這樣我們更容易復(fù)現(xiàn)線程被打滿的情況。線程滿了就會(huì)影響到payment#createByOrder接口。
image-20210415103018785 上面我們壓測(cè)的是payment的原生接口。如果壓測(cè)的是order模塊。如果沒有在openfeign中配置fallback。那么order服務(wù)就會(huì)因?yàn)閜ayment#getTimeOut/id接口并發(fā)導(dǎo)致線程滿了從而導(dǎo)致order模塊響應(yīng)緩慢。這就是雪崩效應(yīng)。下面我們從兩個(gè)方面來(lái)解決雪崩的發(fā)生。
業(yè)務(wù)隔離
上面的場(chǎng)景發(fā)生是因?yàn)閜ayment#createByOrder 和payment#getTimeOut/id同屬于payment服務(wù)。一個(gè)payment服務(wù)實(shí)際上就是一個(gè)Tomcat服務(wù)。同一個(gè)tomcat服務(wù)是有一個(gè)線程池的。 每次請(qǐng)求落到該tomcat 服務(wù)里就會(huì)去線程池中申請(qǐng)線程。獲取到線程了才能由線程來(lái)處理請(qǐng)求的業(yè)務(wù)。就是因?yàn)閠omcat內(nèi)共享線程池。所以當(dāng)payment#getTimeOut/id并發(fā)上來(lái)后就會(huì)搶空線程池。導(dǎo)致別的借口甚至是毫不相關(guān)的接口都沒有資源可以申請(qǐng)。只能干巴巴的等待資源的釋放。
這就好比上班高峰期乘坐電梯因?yàn)槟骋粋€(gè)公司集中上班導(dǎo)致一段時(shí)間電梯全部被使用了。這時(shí)候國(guó)家領(lǐng)導(dǎo)過(guò)來(lái)也沒辦法上電梯。
我們也知道這種情況很好解決。每個(gè)園區(qū)都會(huì)有專用電梯供特殊使用。
我們解決上述問(wèn)題也是同樣的思路。進(jìn)行隔離。不同的接口有不同的線程池。這樣就不會(huì)造成雪崩。
線程隔離

-
還記得我們上面為了演示并發(fā)將order模塊的最大線程數(shù)設(shè)置為10.這里我們通過(guò)測(cè)試工具調(diào)用下order/getpayment/1這個(gè)接口看看日志打印情況
image-20210415142629374 我們接口調(diào)用的地方將當(dāng)前線程打印出來(lái)。我們可以看到一只都是那10個(gè)線程在來(lái)回的使用。這也是上面為什么會(huì)造成雪崩現(xiàn)象。
@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("已經(jīng)進(jìn)入備選方案了,下面交由自由線程執(zhí)行"+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);
}
- 這里演示效果不好展示,我就直接展示數(shù)據(jù)吧。
| 并發(fā)量在getpaymentTimeout | getpaymentTimeout/{id} | /getpayment/{id} |
|---|---|---|
| 20 | 三個(gè)線程打滿后一段時(shí)間開始報(bào)錯(cuò) | 可以正常響應(yīng);也會(huì)慢,cpu線程切換需要時(shí)間 |
| 30 | 同上 | 同上 |
| 50 | 同上 | 也會(huì)超時(shí),因?yàn)閛rder調(diào)用payment服務(wù)壓力會(huì)受影響 |
- 如果我們將hystrix加載payment原生服務(wù)就不會(huì)出現(xiàn)上面第三條情況。為什么我會(huì)放在order上就是想讓大家看看雪崩的場(chǎng)景。在并發(fā)50的時(shí)候因?yàn)閜ayment設(shè)置的最大線程也是10,他本身也是有吞吐量的。在order#getpyament/id接口雖然在order模塊因?yàn)閔ystrix線程隔離有自己的線程運(yùn)行,但是因?yàn)樵?wù)不給力導(dǎo)致自己調(diào)用超時(shí)從而影響運(yùn)行的效果。這樣演示也是為了后續(xù)引出fallback解決雪崩的一次場(chǎng)景模擬吧。
- 我們可以在payment服務(wù)中通過(guò)hystrix設(shè)置fallback。保證payment服務(wù)低延遲從而保證order模塊不會(huì)因?yàn)閜ayment自己緩慢導(dǎo)致order#getpayment這種正常接口異常。
- 還有一點(diǎn)雖然通過(guò)hystrix進(jìn)行線程隔離了。但是我們?cè)谶\(yùn)行其他接口時(shí)響應(yīng)時(shí)間也會(huì)稍長(zhǎng)點(diǎn)。因?yàn)镃PU在進(jìn)行線程切換的時(shí)候是有開銷的。這一點(diǎn)也是痛點(diǎn)。我們并不能隨心所欲的進(jìn)行線程隔離的。這就引出我們的信號(hào)量隔離了。
信號(hào)量隔離
- 關(guān)于信號(hào)量隔離這里也就不演示了。演示的意義不是很大
@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"
)
- 我們?nèi)缟吓渲帽硎拘盘?hào)量最大為6 。 表示并發(fā)6之后就會(huì)進(jìn)行等待。等待超時(shí)時(shí)間未1s。
| 措施 | 優(yōu)點(diǎn) | 缺點(diǎn) | 超時(shí) | 熔斷 | 異步 |
|---|---|---|---|---|---|
| 線程隔離 | 一個(gè)調(diào)用一個(gè)線程池;互相不干擾;保證高可用 | cpu線程切換開銷 | √ | √ | √ |
| 信號(hào)量隔離 | 避免CPU切換。高效 | 在高并發(fā)場(chǎng)景下需要存儲(chǔ)信號(hào)量變大 | × | √ | × |
- 除了線程隔離、信號(hào)量隔離等隔離手段我們可以通過(guò)請(qǐng)求合并、接口數(shù)據(jù)緩存等手段加強(qiáng)穩(wěn)定性。
服務(wù)降級(jí)
觸發(fā)條件
- 程序發(fā)生除HystrixBadRequestException異常。
- 服務(wù)調(diào)用超時(shí)
- 服務(wù)熔斷
- 線程池、信號(hào)量不夠

在上面我們的timeout接口。不管是線程隔離還是信號(hào)量隔離在條件滿足的時(shí)候就會(huì)直接拒絕后續(xù)請(qǐng)求。這樣太粗暴了。上面我們也提到了fallback。
-
還記的上面我們order50個(gè)并發(fā)的timeout的時(shí)候會(huì)導(dǎo)致getpayment接口異常,當(dāng)時(shí)定位了是因?yàn)樵鷓ayment服務(wù)壓力撐不住導(dǎo)致的。如果我們?cè)趐ayment上加入fallback就能保證在資源不足的時(shí)候也能快速響應(yīng)。這樣至少能保證order#getpayment方法的可用性。
image-20210415180423428但是這種配置屬于實(shí)驗(yàn)性配置。在真實(shí)生產(chǎn)中我們不可能在每個(gè)方法上配置fallback的。這樣愚蠢至極。
-
hystrix除了在方法上特殊定制的fallback以外,還有一個(gè)全局的fallback。只需要在類上通過(guò)
@DefaultProperties(defaultFallback = "globalFallback")來(lái)實(shí)現(xiàn)全局的備選方案。一個(gè)方法滿足觸發(fā)降級(jí)的條件時(shí)如果該請(qǐng)求對(duì)應(yīng)的HystrixCommand注解中沒有配置fallback則使用所在類的全局fallback。如果全局也沒有則拋出異常。不足
- 雖然
DefaultProperties可以避免每個(gè)接口都配置fallback。但是這種的全局好像還不是全局的fallback。我們還是需要每個(gè)類上配置fallback。筆者查閱了資料好像也沒有 - 但是在openfeign專題里我們說(shuō)了openfeign結(jié)合hystrix實(shí)現(xiàn)的服務(wù)降級(jí)功能。還記的里面提到了一個(gè)
FallbackFactory這個(gè)類嗎。這個(gè)類可以理解成spring的BeanFactory。這個(gè)類是用來(lái)產(chǎn)生我們所需要的FallBack的。我們?cè)谶@個(gè)工廠里可以生成一個(gè)通用類型的fallback的代理對(duì)象。代理對(duì)象可以根據(jù)代理方法的方法簽名進(jìn)行入?yún)⒑统鰠ⅰ?/li> - 這樣我們可以在所有的openfeign地方配置這個(gè)工廠類。這樣的話就避免的生成很多個(gè)fallback。 美中不足的還是需要每個(gè)地方都指定一下。關(guān)于
FallBackFactory感興趣的可以下載源碼查看或者進(jìn)主頁(yè)查看openfeign專題。
- 雖然
服務(wù)熔斷
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否開啟斷路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //請(qǐng)求次數(shù)
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //時(shí)間范圍
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失敗率達(dá)到多少后跳閘
},
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();
}
- 首先我們通過(guò)circuitBreaker.enabled=true開啟熔斷器
-
circuitBreaker.requestVolumeThreshold設(shè)置統(tǒng)計(jì)請(qǐng)求次數(shù) -
circuitBreaker.sleepWindowInMilliseconds設(shè)置時(shí)間滑動(dòng)單位 , 在觸發(fā)熔斷后多久進(jìn)行嘗試開放,及俗稱的半開狀態(tài) -
circuitBreaker.errorThresholdPercentage設(shè)置觸發(fā)熔斷開關(guān)的臨界條件 - 上面的配置如果最近的10次請(qǐng)求錯(cuò)誤率達(dá)到60% ,則觸發(fā)熔斷降級(jí) , 在10S內(nèi)都處于熔斷狀態(tài)服務(wù)進(jìn)行降級(jí)。10S后半開嘗試獲取服務(wù)最新狀態(tài)
- 下面我們通過(guò)jmeter進(jìn)行接口
http://localhost/order/get?id=-1進(jìn)行20次測(cè)試。雖然這20次無(wú)一例額外都會(huì)報(bào)錯(cuò)。但是我們會(huì)發(fā)現(xiàn)一開始報(bào)錯(cuò)是因?yàn)槲覀兇a里的錯(cuò)誤。后面的錯(cuò)誤就是hystrix熔斷的錯(cuò)誤了。一開始試by zero 錯(cuò)誤、后面就是short-circuited and fallback failed 熔斷錯(cuò)誤了

- 正常我們?cè)趆ystrix中會(huì)配置fallback , 關(guān)于fallback兩種方式我們上面降級(jí)章節(jié)已經(jīng)實(shí)現(xiàn)了。這里是為了方便看到錯(cuò)誤的不同特意放開了。

- 在HystrixCommand中配置的參數(shù)基本都是在HystrixPropertiesManager對(duì)象中。我們可以看到關(guān)于熔斷器的配置有6個(gè)參數(shù)?;揪褪俏覀兩厦娴乃膫€(gè)配置
服務(wù)限流
- 服務(wù)降級(jí)我們上面提到的兩種隔離就是實(shí)現(xiàn)限流的策略。
請(qǐng)求合并
- 除了熔斷、降級(jí)、限流意外hystrix還為我們提供了請(qǐng)求合并。顧名思義就是將多個(gè)請(qǐng)求合并成一個(gè)請(qǐng)求已達(dá)到降低并發(fā)的問(wèn)題。
- 比如說(shuō)我們order有個(gè)接個(gè)是查詢當(dāng)個(gè)訂單信息
order/getId?id=1突然有一萬(wàn)個(gè)請(qǐng)求過(guò)來(lái)。為了緩解壓力我們集中一下請(qǐng)求每100個(gè)請(qǐng)求調(diào)用一次order/getIds?ids=xxxxx。這樣我們最終到payment模塊則是10000/100=100個(gè)請(qǐng)求。下面我們通過(guò)代碼配置實(shí)現(xiàn)下請(qǐng)求合并。
HystrixCollapser
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HystrixCollapser {
String collapserKey() default "";
String batchMethod();
Scope scope() default Scope.REQUEST;
HystrixProperty[] collapserProperties() default {};
}
| 屬性 | 含義 |
|---|---|
| collapserKey | 唯一標(biāo)識(shí) |
| batchMethod | 請(qǐng)求合并處理方法。即合并后需要調(diào)用的方法 |
| scope | 作用域;兩種方式[REQUEST, GLOBAL] ; <br />REQUEST : 在同一個(gè)用戶請(qǐng)求中達(dá)到條件將會(huì)合并<br />GLOBAL : 任何線程的請(qǐng)求都會(huì)加入到這個(gè)全局統(tǒng)計(jì)中 |
| HystrixProperty[] | 配置相關(guān)參數(shù) |

- 在Hystrix中所有的properties配置都會(huì)在HystrixPropertiesManager.java中。我們?cè)诶锩婵梢哉业紺ollapser只有兩個(gè)相關(guān)的配置。分別表示最大請(qǐng)求數(shù)和統(tǒng)計(jì)時(shí)間單元。
@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);
}
- 上面我們配置了getId會(huì)走getIds請(qǐng)求,最多是10S三個(gè)請(qǐng)求會(huì)合并在一起。然后getIds有payment服務(wù)在分別去查詢最終返回多個(gè)ResultInfo。

我們通過(guò)jemeter進(jìn)行g(shù)etId接口壓測(cè),日志中ids的長(zhǎng)度最大是3 。 驗(yàn)證了我們上面getId接口的配置。這樣就能保證在出現(xiàn)高并發(fā)的時(shí)候會(huì)進(jìn)行接口合并降低TPS。
上面我們是通過(guò)請(qǐng)求方法注解進(jìn)行接口合并處理。實(shí)際上內(nèi)部hystrix是通過(guò)HystrixCommand
工作流程

官網(wǎng)給出的流程圖示,并配備流程說(shuō)明一共是9部。下面我們就翻譯下。
-
①、創(chuàng)建HystrixCommand或者HystrixObservableCommand對(duì)象
- HystrixCommand : 用在依賴單個(gè)服務(wù)上
- HystrixObservableCommand : 用在依賴多個(gè)服務(wù)上
②、命令執(zhí)行,hystrrixCommand 執(zhí)行execute、queue ; hystrixObservableCommand執(zhí)行observe、toObservable
| 方法 | 作用 |
|---|---|
| execute | 同步執(zhí)行;返回結(jié)果對(duì)象或者異常拋出 |
| queue | 異步執(zhí)行;返回Future對(duì)象 |
| observe | 返回Observable對(duì)象 |
| toObservable | 返回Observable對(duì)象 |
- ③、查看緩存是否開啟及是否命中緩存,命中則返回緩存響應(yīng)
- ④、是否熔斷, 如果已經(jīng)熔斷則fallback降級(jí);如果熔斷器是關(guān)閉的則放行
- ⑤、線程池、信號(hào)量是否有資源供使用。如果沒有足夠資源則fallback 。 有則放行
- ⑥、執(zhí)行run或者construct方法。這兩個(gè)是hystrix原生的方法,java實(shí)現(xiàn)hystrix會(huì)實(shí)現(xiàn)兩個(gè)方法的邏輯,springcloud已經(jīng)幫我們封裝了。這里就不看這兩個(gè)方法了。如果執(zhí)行錯(cuò)誤或者超時(shí)則fallback。在此期間會(huì)將日志采集到監(jiān)控中心。
- ⑦、計(jì)算熔斷器數(shù)據(jù),判斷是否需要嘗試放行;這里統(tǒng)計(jì)的數(shù)據(jù)會(huì)在hystrix.stream的dashboard中查看到。方便我們定位接口健康狀態(tài)
- ⑧、在流程圖中我們也能看到④、⑤、⑥都會(huì)指向fallback。 也是我們俗稱的服務(wù)降級(jí)??梢姺?wù)降級(jí)是hystrix熱門業(yè)務(wù)啊。
- ⑨、返回響應(yīng)
HystrixDashboard
hystrix 除了服務(wù)熔斷、降級(jí)、限流以外,還有一個(gè)重要的特性是實(shí)時(shí)監(jiān)控。并形成報(bào)表統(tǒng)計(jì)接口請(qǐng)求信息。
關(guān)于hystrix的安裝也很簡(jiǎn)單,只需要在項(xiàng)目中配置actutor和
hystrix-dashboard兩個(gè)模塊就行了
<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>
- 啟動(dòng)類上添加
EnableHystrixDashboard就引入了dashboard了。 我們不需要進(jìn)行任何開發(fā)。這個(gè)和eureka一樣主需要簡(jiǎn)單的引包就可以了。

就這樣dashboard搭建完成了。dashboard主要是用來(lái)監(jiān)控hystrix的請(qǐng)求處理的。所以我們還需要在hystrix請(qǐng)求出將端點(diǎn)暴露出來(lái)。
在使用了hystrix命令的模塊加入如下配置即可,我就在order模塊加入
@Component
public class HystrixConfig {
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
//注意這里配置的/hystrix.stream 最終訪問(wèn)地址就是 localhost:port/hystrix.stream ; 如果在配置文件中配置在新版本中是需要
//加上actuator 即 localhost:port/actuator
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
- 然后我們?cè)L問(wèn)order模塊
localhost/hystrix.stream就會(huì)出現(xiàn)ping的界面。表示我們order模塊安裝監(jiān)控成功。當(dāng)然order也需要actuator模塊 - 下面我們通過(guò)jmeter來(lái)壓測(cè)我們的熔斷、降級(jí)、限流接口在通過(guò)dashboard來(lái)看看各個(gè)狀態(tài)吧。

- 上面的動(dòng)畫看起來(lái)我們的服務(wù)還是很忙的。想想如果是電商當(dāng)你看著每個(gè)接口的折線圖像不像就是你的心跳。太高的你就擔(dān)心的。太低了就沒有成就高。下面我們看看dashboard的指標(biāo)詳情

- 我們看看我們服務(wù)運(yùn)行期間各個(gè)接口的現(xiàn)狀。

聚合監(jiān)控
- 上面我們通過(guò)新建的模塊
hystrix-dashboard來(lái)對(duì)我們的order模塊進(jìn)行監(jiān)控。但是實(shí)際應(yīng)用中我們不可能只在order中配置hystrix的。 - 我們只是在上面為了演示所以在order配置的?,F(xiàn)在我們?cè)趐ayment中也對(duì)hystrix中配置。那么我們就需要在dashboard中來(lái)回切換order、payment的監(jiān)控?cái)?shù)據(jù)了。
- 所以我們的聚合監(jiān)控就來(lái)了。在進(jìn)行聚合監(jiān)控之前我們先將payment也引入hystrix。注意上面我們是通過(guò)bean方式注入hystrix.stream 的 。 訪問(wèn)前綴不需要actuator
新建hystrix-turbine
pom
<!--新增hystrix dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
- 主要就是新增turbine坐標(biāo),其他的就是hystrix , dashboard等模塊,具體查看結(jié)尾處源碼
yml
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
# 聚合監(jiān)控
turbine:
app-config: cloud-order-service,cloud-payment-service
cluster-name-expression: "'default'"
# 該處配置和url一樣。如果/actuator/hystrix.stream 的則需要配置actuator
instanceUrlSuffix: hystrix.stream
啟動(dòng)類
啟動(dòng)類上添加EnableTurbine注解

源碼






