在上一篇文章中,我們已使用Spring Cloud和Netflix OSS中的核心元件,如Eureka、Ribbon和Zuul,部分實作了操作模型(operations model),允許單獨部署的微服務互相通信。在本文中,我們繼續關注微服務環境中的故障處理,通過Hystrix(Netflix Circuit Breaker)提升服務彈性。
現在我們建立的系統開始出現故障,組合服務(composite service)依賴的部分核心服務突然沒有反應,如果故障沒有正确處理,将進一步損害組合服務。
通常,我們将這一類問題稱為失敗鍊(a chain of failures),一個元件中的錯誤将導緻依賴于錯誤元件中的其他元件也産生錯誤。在基于微服務的系統中,尤其是大量獨立部署的微服務互相通信,這一情況需要特别關注。針對這一問題的通用解決方案是應用電路斷路器模式(circuit breaker pattern),詳細資訊可以查閱其他文檔,或者閱讀Fowler-Circuit Breaker的文章。一個典型電路斷路器應用如下狀态轉換圖:
(Source: Release It!) https://pragprog.com/book/mnee/release-it
如下表所示,本文将包含:Hystrix、Hystrix dashboard和Turbine。
1/ Netflix Hystrix – 電路斷路器(Circuit Breaker)
Netflix Hystrix 對微服務消費方提供了電路斷路器功能。如果一個服務沒有響應(如逾時或者網絡連接配接故障),Hystrix 可以在服務消費方中重定向請求到回退方法(fallback method)。如果服務重複失敗,Hystrix 會打開電路,并快速失敗(如直接調用内部的回退方法,不再嘗試調用服務),直到服務重新恢複正常。
為了驗證是否服務再次恢複正常,即使在電路打開的情況下,Hystrix 也會允許一部分請求再次調用微服務。Hystrix 是嵌入在服務調用方内部執行的。
2/ Netflix Hystrix dashboard和Netflix Turbine –監控儀表盤(Monitor Dashboard)
Hystrix儀表盤用來提供電路斷路器的圖形化視圖;Turbine 基于Eureka伺服器的資訊,擷取系統中所有電路斷路器的資訊,提供給儀表盤。下圖是Hystrix儀表盤和Turbine工作視圖:
将前一部分Part 1實作的微服務系統,進一步添加支援性的基礎服務-Hystrix Dashboard和Turbine。另外,微服務product-composite也增強了基于Hystrix的電路斷路器。新增的2個元件辨別為紅色外框,如下圖所示:
在Part 1中,我們重點強調了微服務和單體應用的差異,将每一個微服務獨立部署運作(獨立程序)
在Part 1中,我們使用Java SE 8,Git和Gradle工具。接下來通路源代碼,并進行編譯:
$ git clone https://github.com/callistaenterprise/blog-microservices.git
$ cd blog-microservices
$ git checkout -b B2 M2.1
$ ./build-all.sh
如果運作在windows平台,請執行相應的bat檔案 – build-all.bat。
在Part 1源碼的基礎上,新增了2個源碼元件-monitor-dashboard和turbine:
編譯輸出8條log日志:
BUILD SUCCESSFUL
和Part 1源代碼進行比較,本文在微服務product-composite中新增了Hystrix電路斷路器的使用。是以,我們将關注電路斷路器部分新增的額外代碼。
4.1 Gradle依賴
現在,我們在build檔案中加入了幾個Hystrix相關的starter依賴。因為Hystrix使用RabbitMQ消息中間件在電路斷路器和儀表盤(dashboard)之間通信,是以我們也需要添加相應的依賴。
對于服務消費方,如需要使用Hystrix作為電路斷路器,則需要添加如下依賴配置:
compile("org.springframework.cloud:spring-cloud-starter-hystrix:1.0.0.RELEASE")
compile("org.springframework.cloud:spring-cloud-starter-bus-amqp:1.0.0.RELEASE")
compile("org.springframework.cloud:spring-cloud-netflix-hystrix-amqp:1.0.0.RELEASE")
更完整的示例,可以檢視product-composite-service/build.gradle檔案。
為了搭建Turbine 伺服器,需要添加如下依賴:
compile('org.springframework.cloud:spring-cloud-starter-turbine-amqp:1.0.0.RELEASE')
更完整的示例,可以檢視turbine/build.gradle檔案。
4.2 基礎設施伺服器
在标準的Spring Boot應用中,添加@EnableTurbineAmqp标注,就可以搭建Turbine伺服器了。
@SpringBootApplication
@EnableTurbineAmqp
@EnableDiscoveryClient
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
完整的示例,可以檢視TurbineApplication.java檔案。
搭建Hystrix儀表盤,則需要添加@EnableHystrixDashboard标注。完整的示例,可以檢視HystrixDashboardApplication.java檔案。
通過上述簡單的标注,就可以獲得預設伺服器配置了。可根據需要使用特定的配置,覆寫預設的配置。
4.3 業務服務
為了啟用Hystrix,需要在Spring Boot應用中添加@EnableCircuitBreaker标注。為了讓Hystrix真實地生效,還需要在Hystrix監控的方法上标注@HystrixCommand,在這個标注中,還可以指定回退方法(fallback method),如下所示:
@HystrixCommand(fallbackMethod = "defaultReviews")
public ResponseEntity<List<Review>> getReviews(int productId) {
...
public ResponseEntity<List<Review>> defaultReviews(int productId) {
在發生錯誤的時候(如調用服務失敗或者逾時),Hystrix會調用回退方法;或者在電路打開的時候,進行快速失敗處理。完整的示例,可以檢視ProductCompositeIntegration.java檔案。
如前所述,Hystrix通過RabbitMQ消息中間件進行内部通信,是以我們在啟動微服務系統之前,需要先安裝并運作RabbitMQ。可以通路如下連結,了解RabbitMQ安裝教程:
https://www.rabbitmq.com/download.html
安裝完成之後,接着啟動RabbitMQ,通過運作RabbitMQ安裝目錄下的sbin子目錄中的rabbitmq-server程式進行啟動。
$ ~/Applications/rabbitmq_server-3.4.3/sbin/rabbitmq-server
RabbitMQ 3.4.3. Copyright (C) 2007-2014 GoPivotal, Inc.
## ## Licensed under the MPL. See http://www.rabbitmq.com/
## ##
########## Logs: /Users/magnus/Applications/rabbitmq_server-3.4.3/sbin/../var/log/rabbitmq/[email protected]
###### ## /Users/magnus/Applications/rabbitmq_server-3.4.3/sbin/../var/log/rabbitmq/[email protected]
##########
Starting broker... completed with 6 plugins.
如在windows系統中,確定RabbitMQ 服務已經啟動。
現在我們準備好啟動系統了。使用./gradlew指令啟動每一個微服務。
首先啟動基礎設施微服務:
$ cd support/discovery-server; ./gradlew bootRun
$ cd support/edge-server; ./gradlew bootRun
$ cd support/monitor-dashboard; ./gradlew bootRun
$ cd support/turbine; ./gradlew bootRun
一旦上述服務啟動完成之後,接着啟動業務微服務:
$ cd core/product-service; ./gradlew bootRun
$ cd core/recommendation-service; ./gradlew bootRun
$ cd core/review-service; ./gradlew bootRun
$ cd composite/product-composite-service; ./gradlew bootRun
如在windows平台,可以執行相應的bat檔案 – start-all.bat。
一旦微服務啟動完成,并注冊到服務發現伺服器(Service Discovery Server),将同時輸出如下日志:
DiscoveryClient ... - registration status: 204
和Part 1 一樣,我們可以在服務發現Web應用中看到如下4個業務服務和一個edge-server,如下所示(http://localhost:8761):
最後,驗證電路斷路器工作正常。在處于closed狀态時,通過edge-server通路組合服務(composite service),輸出響應結果如下:
$ curl -s localhost:8765/productcomposite/product/1 | jq .
{
"name": "name",
"productId": 1,
"recommendations": [
{
"author": "Author 1",
"rate": 1,
"recommendationId": 0
},
],
"reviews": [
"reviewId": 1,
"subject": "Subject 1"
"weight": 123
在浏覽器中首先通路http://localhost:7979 位址(Hystrix Dashboard),接着在文本框中輸入 http://localhost:8989/turbine.stream,并點選Monitor Stream 按鈕:
我們看到組合服務有3個電路斷路器正在運作中,分别是3個依賴的核心服務。目前都工作正常。接着,我們準備嘗試故障測試,驗證電路斷路器發揮作用。
停止review微服務,再次嘗試之前的指令:
"reviews": null,
傳回的響應封包中review部分是空的,但是其餘部分封包保持不變。檢視product-composite服務日志,可以發現如下警告資訊:
2015-04-02 15:13:36.344 INFO 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : GetRecommendations...
2015-04-02 15:13:36.497 INFO 29901 --- [teIntegration-2] s.c.m.c.p.s.ProductCompositeIntegration : GetReviews...
2015-04-02 15:13:36.498 WARN 29901 --- [teIntegration-2] s.c.m.composite.product.service.Util : Failed to resolve serviceId 'review'. Fallback to URL 'http://localhost:8081/review'.
2015-04-02 15:13:36.500 WARN 29901 --- [teIntegration-2] s.c.m.c.p.s.ProductCompositeIntegration : Using fallback method for review-service
電路斷路器檢測到review服務發生了故障,将請求路由到服務消費方的回退方法(fallback method)。在本示例中,我們隻是簡單地傳回一個null,但我們也可以傳回一個本地緩存資料,以便在review服務發生故障時,提供更好的效果。
因為此時故障發生頻率并不高,是以電路仍然是閉合狀态(closed):
我們接下來提高故障頻率,并超出Hystrix打開電路的限制,開始快速失敗。這裡,我們使用Apache HTTP server benchmarking tool是實作這一目的:
ab -n 30 -c 5 localhost:8765/productcomposite/product/1
現在電路打開了:
随後的請求将快速失敗,也就是說,電路斷路器将直接轉發請求到回退方法,不再調用review服務。此時,log日志中将不再有GetReviews相關日志。
2015-04-02 15:14:03.930 INFO 29901 --- [teIntegration-5] s.c.m.c.p.s.ProductCompositeIntegration : GetRecommendations...
2015-04-02 15:14:03.984 WARN 29901 --- [ XNIO-2 task-62] s.c.m.c.p.s.ProductCompositeIntegration : Using fallback method for review-service
然而,Hystrix不時地讓一部分請求通過電路,檢視是否可以調用成功,也就是檢查review服務是否再次恢複正常。我們可以多次重複執行curl調用,檢視product-composite服務的輸出日志:
2015-04-02 15:17:33.587 INFO 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : GetRecommendations...
2015-04-02 15:17:33.769 INFO 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : GetReviews...
2015-04-02 15:17:33.769 WARN 29901 --- [eIntegration-10] s.c.m.composite.product.service.Util : Failed to resolve serviceId 'review'. Fallback to URL 'http://localhost:8081/review'.
2015-04-02 15:17:33.770 WARN 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : Using fallback method for review-service
2015-04-02 15:17:34.431 INFO 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : GetRecommendations...
2015-04-02 15:17:34.569 WARN 29901 --- [ XNIO-2 task-18] s.c.m.c.p.s.ProductCompositeIntegration : Using fallback method for review-service
2015-04-02 15:17:35.209 INFO 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : GetRecommendations...
2015-04-02 15:17:35.402 WARN 29901 --- [ XNIO-2 task-20] s.c.m.c.p.s.ProductCompositeIntegration : Using fallback method for review-service
2015-04-02 15:17:36.043 INFO 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : GetRecommendations...
2015-04-02 15:17:36.192 WARN 29901 --- [ XNIO-2 task-21] s.c.m.c.p.s.ProductCompositeIntegration : Using fallback method for review-service
2015-04-02 15:17:36.874 INFO 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : GetRecommendations...
2015-04-02 15:17:37.031 WARN 29901 --- [ XNIO-2 task-22] s.c.m.c.p.s.ProductCompositeIntegration : Using fallback method for review-service
2015-04-02 15:17:41.148 INFO 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : GetRecommendations...
2015-04-02 15:17:41.340 INFO 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : GetReviews...
2015-04-02 15:17:41.340 WARN 29901 --- [eIntegration-10] s.c.m.composite.product.service.Util : Failed to resolve serviceId 'review'. Fallback to URL 'http://localhost:8081/review'.
2015-04-02 15:17:41.341 WARN 29901 --- [eIntegration-10] s.c.m.c.p.s.ProductCompositeIntegration : Using fallback method for review-service
從log日志的輸出中,我們發現每5個調用,允許一次嘗試調用review服務(仍然沒有成功調用)。
現在,我們再次啟動review服務,繼續嘗試調用組合服務product-composite。
備注:此時,你可能需要一點耐心(最多1分鐘)。在調用成功之前,需要服務發現伺服器(Eureka)和動态路由(Ribbon)必須感覺到review服務執行個體再次恢複可用。
現在,我們看到傳回結果正常了,review節點也恢複到傳回封包中,電路也再次閉合(closed):
我們已經看到了Netflix Hystrix如何用作電路斷路器(Circuit Breaker)有效地處理失敗鍊的問題。失敗鍊是指:當單個微服務故障時,由于故障的擴散,會導緻系統中大範圍微服務故障事故。幸虧Spring Cloud架構提供的簡單标注和starter依賴,可以非常容易在Spring環境中啟用Hystrix。最後,Hystrix dashboard和Turbine提供的儀表盤(Dashboard)功能,使得監控系統範圍内的大量電路斷路器變得切實可行。
在建構微服務的下一篇文章中,我們将學習如何使用OAuth 2.0 來限制對暴露為外部API的微服務進行通路。
英文原文連結:
Building microservices with Spring Cloud and Netflix OSS, part 2
相關連結:
基于Spring Cloud和Netflix OSS 建構微服務-Part 1
微服務操作模型