基於Spring Cloud和Netflix OSS構建微服務,Part 2

Rickie發表於2017-03-18

在上一篇文章中,我們已使用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

微服務操作模型

 

相關文章