在上一篇文章中,我們已使用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
1. Spring Cloud和Netflix OSS
如下表所示,本文將包含: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工作檢視:
2. 系統全貌
將前一部分Part 1實現的微服務系統,進一步新增支援性的基礎服務-Hystrix Dashboard和Turbine。另外,微服務product-composite也增強了基於Hystrix的電路斷路器。新增的2個元件標識為紅色外框,如下圖所示:
在Part 1中,我們重點強調了微服務和單體應用的差異,將每一個微服務獨立部署執行(獨立程式)
3. 構建原始碼
在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
4. 閱讀原始碼
和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檔案。
5. 啟動系統
如前所述,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/rabbit@Magnus-MacBook-Pro.log
###### ## /Users/magnus/Applications/rabbitmq_server-3.4.3/sbin/../var/log/rabbitmq/rabbit@Magnus-MacBook-Pro-sasl.log
##########
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": [
{
"author": "Author 1",
"reviewId": 1,
"subject": "Subject 1"
},
...
],
"weight": 123
}
在瀏覽器中首先訪問http://localhost:7979 地址(Hystrix Dashboard),接著在文字框中輸入 http://localhost:8989/turbine.stream,並點選Monitor Stream 按鈕:
我們看到組合服務有3個電路斷路器正在執行中,分別是3個依賴的核心服務。目前都工作正常。接著,我們準備嘗試故障測試,驗證電路斷路器發揮作用。
6. 發生故障
停止review微服務,再次嘗試之前的命令:
$ curl -s localhost:8765/productcomposite/product/1 | jq .
{
"name": "name",
"productId": 1,
"recommendations": [
{
"author": "Author 1",
"rate": 1,
"recommendationId": 0
},
...
],
"reviews": null,
"weight": 123
}
返回的響應報文中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):
7. 總結
我們已經看到了Netflix Hystrix如何用作電路斷路器(Circuit Breaker)有效地處理失敗鏈的問題。失敗鏈是指:當單個微服務故障時,由於故障的擴散,會導致系統中大範圍微服務故障事故。幸虧Spring Cloud框架提供的簡單標註和starter依賴,可以非常容易在Spring環境中啟用Hystrix。最後,Hystrix dashboard和Turbine提供的儀表盤(Dashboard)功能,使得監控系統範圍內的大量電路斷路器變得切實可行。
8. 接下來
在構建微服務的下一篇文章中,我們將學習如何使用OAuth 2.0 來限制對暴露為外部API的微服務進行訪問。
英文原文連結:
Building microservices with Spring Cloud and Netflix OSS, part 2
相關連結:
基於Spring Cloud和Netflix OSS 構建微服務-Part 1