微服務框架Spring Cloud介紹 Part5: 在微服務系統中使用Hystrix, Hystrix Dashboard與Turbine

原文地址:http://skaka.me/blog/2016/09/04/springcloud5/

通過前面幾篇文章的介紹, 我們已經能夠使用Spring Cloud開發出一個簡單的系統了. 這篇文章裏, 我們將關注點轉移到如何提高微服務系統的容錯性(Fault Tolerance), 並瞭解如何藉助Hystrix開發健壯的微服務.

以往我們在開發單體應用, 或者調用RPC服務的時候, 可能沒有考慮太多目標服務調用失敗的情況, 經常一個Try/Catch加上打印日誌就解決了. 但是在微服務系統中, 這種處理方法會給系統的穩定性帶來很大隱患.舉個例子, 假設我們系統中下單的功能要依賴50個服務, 每個服務正常響應的概率爲99.99%, 如果我們不做容錯處理, 只要任意一個服務沒有響應下單就失敗的話, 我們下單成功的概率爲

99.9950 = 99.5%

日訂單量爲1W的話, 50個會出現下單失敗, 這還是建立在依賴服務穩定性很高的情況下(4個9). 但是服務調用失敗引起的問題不僅僅是這麼簡單, 在分佈式環境下, 一個服務的調用失敗可能會使其他被依賴服務發生延遲和超時, 而且這個影響會很快擴散到其他服務, 從而引發整個系統的雪崩(Avalanche).

1. hystrix介紹

這篇文章要介紹的Hystrix是一個Java類庫, 它提供下面這些功能來幫助我們構建健壯的微服務系統:(對Hystrix已經比較熟悉的同學可以直接跳過這段到下面的Hystrix javanica介紹)
1.斷路器機制
斷路器很好理解, 當Hystrix Command請求後端服務失敗數量超過一定比例(默認50%), 斷路器會切換到開路狀態(Open). 這時所有請求會直接失敗而不會發送到後端服務. 斷路器保持在開路狀態一段時間後(默認5秒), 自動切換到半開路狀態(HALF-OPEN). 這時會判斷下一次請求的返回情況, 如果請求成功, 斷路器切回閉路狀態(CLOSED), 否則重新切換到開路狀態(OPEN). Hystrix的斷路器就像我們家庭電路中的保險絲, 一旦後端服務不可用, 斷路器會直接切斷請求鏈, 避免發送大量無效請求影響系統吞吐量, 並且斷路器有自我檢測並恢復的能力.
2.Fallback
Fallback相當於是降級操作. 對於查詢操作, 我們可以實現一個fallback方法, 當請求後端服務出現異常的時候, 可以使用fallback方法返回的值. fallback方法的返回值一般是設置的默認值或者來自緩存.
3.資源隔離
在Hystrix中, 主要通過線程池來實現資源隔離. 通常在使用的時候我們會根據調用的遠程服務劃分出多個線程池. 例如調用產品服務的Command放入A線程池, 調用賬戶服務的Command放入B線程池. 這樣做的主要優點是運行環境被隔離開了. 這樣就算調用服務的代碼存在bug或者由於其他原因導致自己所在線程池被耗盡時, 不會對系統的其他服務造成影響. 但是帶來的代價就是維護多個線程池會對系統帶來額外的性能開銷. 如果是對性能有嚴格要求而且確信自己調用服務的客戶端代碼不會出問題的話, 可以使用Hystrix的信號模式(Semaphores)來隔離資源.

以上是對Hystrix的簡單介紹, 如果想進一步瞭解Hystrix可以訪問GitHub. 現在我們來看如何編寫一個Hystrix Command, 代碼來自Hystrix的Github:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CommandHelloFailure extends HystrixCommand<String> {

    private final String name;

    public CommandHelloFailure(String name) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                      .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
                      .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));
        this.name = name;
    }

    @Override
    protected String run() {
        return "hello world";
    }

    @Override
    protected String getFallback() {
        return "Hello Failure " + name + "!";
    }
}

編寫一個Hystrix Command, 需要繼承HystrixCommand類, 在run方法中完成你的業務邏輯. 你可以重寫getFallback方法來在run方法拋出異常的時候返回備用的結果.

2. Hystrix javanica介紹

上面的代碼寫起來有點繁瑣, 好在有javanica. 這是Hystrix開源社區貢獻的一個類庫. 使用javanica, 你不需要繼承HystrixCommand, 只要在方法上添加@HystrixCommand註解, 你的方法就能夠通過Hystrix運行. 例如下面的代碼:

1
2
3
4
5
6
7
8
9
@HystrixCommand(groupKey="ExampleGroup", commandKey = "HelloWorld",
         threadPoolKey="HelloWorldPool", fallbackMethod = "defaultHello")
public String getUserById(String name) {
    return "hello world";
}

private String defaultHello(String name) {
    return "Hello Failure " + name + "!";
}

這段代碼與之前的繼承HystrixCommand代碼所完成的工作是一樣的, 如果習慣了使用Java註解和Spring, 一般會更習慣javanica的開發方式.

3. 在Spring Cloud中使用Hystrix

瞭解了Hystrix和javanica, 現在我們來看看如何在基於Spring Cloud的項目中使用Hystrix.

1. 添加maven依賴

在pom.xml文件中加入下面的依賴

1
2
3
4
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

mysteam項目裏, 依賴被加到了$YOUR_PATH/mysteam/common/pom.xml文件中.

2. Spring配置加上@EnableHystrix註解

BaseConfiguration類中加了這個註解, 文件位置在$YOUR_PATH/mysteam/common/src/main/java/com/akkafun/common/spring/BaseConfiguration.java.

3. 方法上添加@HystrixCommand註解

上篇文章中我們開發了下單的功能. 爲了查詢賬戶餘額是否足夠, 我們調用了AccountGatewayisBalanceEnough方法:

1
2
3
4
@HystrixCommand(ignoreExceptions = RemoteCallException.class)
public boolean isBalanceEnough(Long userId, Long amount) {
    return accountClient.checkEnoughBalance(userId, amount).isSuccess();
}

這裏我們沒有指定commandKeygroupKey參數, commandKey的默認值是方法名稱isBalanceEnoughgroupKey的默認值是類名AccountGateway. javanica使用AOP來完成普通方法到Hystrix Command的轉換, 所以我們只需要在方法上加上@HystrixCommand註解, 就能讓這個方法成爲一個Hystrix Command了, 相當便捷. 這裏說明一下ignoreExceptions = RemoteCallException.class這個配置的含義. 在Hystrix Command的run方法執行的時候, 如果拋出了HystrixBadRequestException異常, 是不會觸發Fallback邏輯而是直接失敗, 這個異常一般被用來提示客戶端參數請求錯誤或者其他需要直接失敗的錯誤.

1
@HystrixCommand(ignoreExceptions = RemoteCallException.class)

這個配置的意思是, 如果方法中拋出了RemoteCallException(這個異常是我們自己定義的), javanica會負責將異常包裝爲HystrixBadRequestException並拋出. 這裏指定RemoteCallException是因爲在mysteam中, 遠程服務返回的正常錯誤信息會被包裝爲RemoteCallException拋出, 因爲是正常的返回, 我們不想讓Hystrix執行Fallback邏輯, 所以在這裏配置了ignoreExceptions參數(如果想了解mysteam的http請求和異常處理邏輯, 可以查看CustomRestTemplate類和RestTemplateErrorHandler類的代碼).

4. 在Spring Cloud中使用Hystrix Dashboard和Turbine

針對生產環境, Netflix還給我們準備了一個非常好用的運維工具, 那就是Hystrix DashboardTurbine.
先上一張圖有個直觀感受, 來自Turbine的GitHub 通過Hystrix Dashboard我們可以在直觀地看到各Hystrix Command的請求響應時間, 請求成功率等數據. 但是隻使用Hystrix Dashboard的話, 你只能看到單個應用內的服務信息, 這明顯不夠. 我們需要一個工具能讓我們彙總系統內多個服務的數據並顯示到Hystrix Dashboard上, 這個工具就是Turbine.

我在mysteam中已經加了一個turbine模塊, 模塊名稱是turbine, 接下來, 讓我們啓動turbine服務.

1. 首先啓動EurekaApplication和ConfigApplication

關於mysteam中爲什麼要先啓動這兩個服務, 可以參看我之前的文章.

2. 啓動turbine模塊下的TurbineApplication

啓動完成之後, 打開鏈接http://localhost:7777/hystrix, 你應該能看見可愛的豪豬logo. 在頁面中部的輸入框輸入你要監控的turbine流地址, 例如輸入 http://localhost:7777/turbine.stream?cluster=ORDER, 代表我們要監控ORDER服務下所有實例的Hystrix. 輸入完成點擊Monitor Stream按鈕, 你就能進入監控UI了. 但是這時應該還沒有顯示圖表, 因爲ORDER服務還沒有啓動. 到此爲止, 如果你只是想啓動Turbine服務, 那麼你已經完成了. 但是如果你想看到實際的監控信息, 還需要啓動其他服務並構造數據. 接下來讓我們啓動其他的5個服務.

3. 啓動AccountApplication, CouponApplication, OrderApplication, ProductApplication, UserApplication
4. 運行integration-test模塊下的OrderIntegrationTest.testCreateOrderSuccess方法生成測試數據

OrderIntegrationTest類的位置在$YOUR_PATH/mysteam/integration-test/core/src/test/java/com/akkafun/integrationtest/order/OrderIntegrationTest.java, 這是一個下單功能的集成測試類. 這個類會模擬真實用戶下單, 所以運行這個測試類之前需要把依賴的服務全部啓動好. testCreateOrderSuccess測試方法運行完成之後, 在瀏覽器裏你應該能看見如下的界面 這張圖裏, Circuit標籤下顯示的是斷路器信息, 其中顯示了ORDER服務的兩個斷路器, 分別是findProductsisBalanceEnough, 並且狀態都是ClosedThread Pools標籤下顯示的是線程池的信息. 這張圖裏各參數的詳細含義, 大家可以參考Dashboard的Wiki. 通過Hystrix Dashboard和Turbine, 我們能夠很方便地監控每個Hystrix Command的運行情況, 在出現問題的時候能夠及時定位到問題所在的服務. Tubine本質是一個數據聚合服務, 我們可以使用Turbine的數據開發一些定製的功能. 比如我之前開發的預警系統, 會實時對Turbine的流式數據進行消費, 在發現Hystrix Command調用失敗次數達到一定閥值的時候, 會根據調用鏈定位到疑似的問題服務併發出告警. 你也可以很容易的將Hystrix Dashbord和Turbine整合到你自己的監控系統裏.

這一篇文章介紹的Hystrix和上一篇文章介紹的Eureka, Ribbon都屬於Spring Cloud Netflix這個子項目的內容. 下一篇裏我們將把目光移到分佈式系統最重要的中間件: MQ, 瞭解如何使用Spring Cloud Stream與Kafka交互.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章