跳閘了啊! 服務容災:熔斷器簡介

二胡嘈子發表於2016-11-03

雪崩效應

現如今SOA、微服務風愈演愈烈,越來越多的業務和資源被以服務的形式包裝和釋出,服務間又可能會依賴其他各種服務。由此而來不可避免的會產生很多問題。

比如一個服務,其依賴了另外30個服務。假設每個服務的可用率都有三個9(99.9%),那麼我們計算一下:

99.99%^30 = 99.7%

現實很殘酷,這個服務的實際可用性只能是99.7%,也就是說每個月這個服務都要好當機8000+秒~~~

跳閘了啊! 服務容災:熔斷器簡介
正常使用者請求時,服務內部依次請求A\P\H\I服務,兵返回響應結果。

跳閘了啊! 服務容災:熔斷器簡介
非常不幸,我們的I服務出了某些問題,此時我們的使用者請求就被堵塞在I服務處。

跳閘了啊! 服務容災:熔斷器簡介
更加悲劇的是,後續越來越多的請求都被堵塞在I服務處,而這些被堵塞的請求會佔用執行緒、IO、網路等系統資源,隨著資源被佔用的越來越多,本來不存在的效能問題也會隨之而來,造成系統中的其他服務出現問題,甚至導致系統奔潰。

這也就是我們常說的雪崩效應


服務容災

為了避免出現服務的雪崩,我們需要對服務做容災處理。

常規的服務容災處理思路有:

  • 資源隔離
  • 超時設定
  • 服務降級
  • 服務限流

其中每種思路又可以有不同的解決方案。

比如資源隔離可以通過將不同的服務釋出在獨立的docker容器或伺服器中,這樣即使一個服務出現問題,也不會殃及池魚。

服務降級和服務限流可以通過前端nginx+lua來實現,當服務處理延遲或當機時,nginx可以直接返回固定的降級/失敗響應,已快速跳過問題服務。


Hystrix

Hystrix,是Netflix的一個開源熔斷器,通過Hystrix,我們可以很方便的實現資源隔離、限流、超時設計、服務降級等服務容災措施,並且還提供了強大的監控,可以檢視各個熔斷器的允許情況。

跳閘了啊! 服務容災:熔斷器簡介

通過上圖,可以看出,Hystrix提供了一個HystrixCommand用來包裝呼叫請求。HystrixCommand的執行流程大概如下:
1.首先檢查快取中是否有結果。如果有則直接返回快取結果。
2.判斷斷路器是否開啟,如果斷路器閉合,執行降級業務邏輯並返回降級結果。
3.判斷訊號量/執行緒池資源是否飽和,如飽和則執行降級業務邏輯並返回降級結果。
4.呼叫實際服務,如發生異常,執行降級業務邏輯並返回降級結果,並調整斷路器閾值。
5.判斷實際業務是否超時,超時則返回超時響應結果,並調整斷路器閾值。

瞭解了流程,來看下如何使用Hystrix。首先我們需要定義一個命令類來包裝我們的業務呼叫:

//繼承HystrixCommand
public class CommandHelloFailure extends HystrixCommand<String> {

    private final String name;

    public CommandHelloFailure(String name) {
        //設定分組key,分組key可以用在報表、監控中,預設的執行緒池隔離也基於分組key
         super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
            //指定命令key,可
            .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
            //指定執行緒池key,取代預設的分組key
            .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));
        /*
        //執行緒池隔離模式,不寫預設是執行緒池隔離模式
        HystrixCommandProperties.Setter()
           .withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD)
        //訊號量隔離模式
        HystrixCommandProperties.Setter()
           .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
        //超時時間,預設1秒
        HystrixCommandProperties.Setter()
           .withExecutionTimeoutInMilliseconds(int value)  
        //訊號量模式下,最大併發請求限流,預設值10
        HystrixCommandProperties.Setter()
           .withFallbackIsolationSemaphoreMaxConcurrentRequests(int value)  
        //熔斷器閾值,預設20
        HystrixCommandProperties.Setter()
            .withCircuitBreakerRequestVolumeThreshold(int value)
        //熔斷器關閉時間,預設5秒
        HystrixCommandProperties.Setter()
            .withCircuitBreakerSleepWindowInMilliseconds(int value)
        */
        this.name = name;
    }

    @Override
    //執行實際服務,這裡模擬全部返回異常
    protected String run() {
        throw new RuntimeException("this command always fails");
    }

    @Override
    //執行降級業務
    protected String getFallback() {
        return "Hello Failure " + name + "!";
    }

    @Override
    //從快取中獲取
    protected String getCacheKey() {
        ...
    }
} 

使用這個命令類也非常簡單:

//同步執行
String s = new CommandHelloWorld("Bob").execute();
//非同步執行
Future<String> s = new CommandHelloWorld("Bob").queue();
//響應式
Observable<String> s = new CommandHelloWorld("Bob").observe();

通過Hystrix提供的監控介面,我們可以觀察到各個熔斷器的執行情況:

跳閘了啊! 服務容災:熔斷器簡介

更多說明和例子可以檢視Hystrix的wiki


Hystrix和Spring boot

想在spring boot中使用Hystrix就更加簡單了,只需要引入spring-cloud-starter-hystrix,

<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>

然後新增註解使用Hystrix
@EnableCircuitBreaker
@EnableHystrixDashboard

最後在需要使用熔斷器的地方標記註解即可。

@HystrixCommand(groupKey = "xxx", fallbackMethod = "yyy")
public String doSomething() 

遙想2015年9月7日,上交所、深交所、中金所宣佈,擬在保留現有個股漲跌幅制度前提下,引入指數熔斷機制。隨後A股聯絡兩天下跌熔斷,提前收盤。其中這裡的熔斷機制和我們今天討論的熔斷器思路一致,但是反而導致了A股暴跌,這也說明了我們還是得從根源產出高可用的服務,而不是依賴某些外部措施幫助我們提高可用性。同時說明了A股比我們寫的垃圾服務更加不可靠,還是安心當個碼農吧。

最後,就問各位童鞋,敢不敢點個贊~~~~

參考資料:
https://github.com/Netflix/Hystrix
http://www.cnblogs.com/jesse2013/p/things-architect-must-know.html

相關文章