Hystrix 服務降級與監控
一:為什麼需要Hystrix?
在大中型分散式系統中,通常系統很多依賴(HTTP,hession,Netty,Dubbo等),如下圖:
在高併發訪問下,這些依賴的穩定性與否對系統的影響非常大,但是依賴有很多不可控問題:如網路連線緩慢,資源繁忙,暫時不可用,服務離線等.
如下圖:QPS為50的依賴 I 出現不可用,但是其他依賴仍然可用.
當依賴I 阻塞時,大多數伺服器的執行緒池就出現阻塞(BLOCK),影響整個線上服務的穩定性.如下圖:
在複雜的分散式架構的應用程式有很多的依賴,都會不可避免地在某些時候失敗。高併發的依賴失敗時如果沒有隔離措施,當前應用服務就有被拖垮的風險。
- 例如:一個依賴30個SOA服務的系統,每個服務99.99%可用。
- 99.99%的30次方 ≈ 99.7%
- 0.3% 意味著一億次請求 會有 3,000,00次失敗
- 換算成時間大約每月有2個小時服務不穩定.
- 隨著服務依賴數量的變多,服務不穩定的概率會成指數性提高.
解決問題方案:對依賴做隔離,Hystrix就是處理依賴隔離的框架,同時也是可以幫我們做依賴服務的治理和監控.
Netflix 公司開發併成功使用Hystrix,使用規模如下:
Java程式碼-
The Netflix API processes 10
- Each API instance has 40+ thread-pools with 5-20 threads in each (most are set to 10).
二:Hystrix如何解決依賴隔離
1:Hystrix使用命令模式HystrixCommand(Command)包裝依賴呼叫邏輯,每個命令在單獨執行緒中/訊號授權下執行。
2:可配置依賴呼叫超時時間,超時時間一般設為比99.5%平均時間略高即可.當呼叫超時時,直接返回或執行fallback邏輯。
3:為每個依賴提供一個小的執行緒池(或訊號),如果執行緒池已滿呼叫將被立即拒絕,預設不採用排隊.加速失敗判定時間。
4:依賴呼叫結果分:成功,失敗(丟擲異常),超時,執行緒拒絕,短路。 請求失敗(異常,拒絕,超時,短路)時執行fallback(降級)邏輯。
5:提供熔斷器元件,可以自動執行或手動呼叫,停止當前依賴一段時間(10秒),熔斷器預設錯誤率閾值為50%,超過將自動執行。
6:提供近實時依賴的統計和監控
Hystrix依賴的隔離架構,如下圖:
三:如何使用Hystrix
1:使用maven引入Hystrix依賴
Html程式碼- <!-- 依賴版本 -->
- <hystrix.version>1.3.16</hystrix.version>
- <hystrix-metrics-event-stream.version>1.1.2</hystrix-metrics-event-stream.version>
- <dependency>
- <groupId>com.netflix.hystrix</groupId>
- <artifactId>hystrix-core</artifactId>
- <version>${hystrix.version}</version>
- </dependency>
- <dependency>
- <groupId>com.netflix.hystrix</groupId>
- <artifactId>hystrix-metrics-event-stream</artifactId>
- <version>${hystrix-metrics-event-stream.version}</version>
- </dependency>
- <!-- 倉庫地址 -->
- <repository>
- <id>nexus</id>
- <name>local private nexus</name>
- <url>http://maven.oschina.net/content/groups/public/</url>
- <releases>
- <enabled>true</enabled>
- </releases>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </repository>
2:使用命令模式封裝依賴邏輯
Java程式碼- public class HelloWorldCommand extends HystrixCommand<String> {
- private final String name;
- public HelloWorldCommand(String name) {
- //最少配置:指定命令組名(CommandGroup)
- super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
- this.name = name;
- }
- @Override
- protected String run() {
- // 依賴邏輯封裝在run()方法中
- return "Hello " + name +" thread:" + Thread.currentThread().getName();
- }
- //呼叫例項
- public static void main(String[] args) throws Exception{
- //每個Command物件只能呼叫一次,不可以重複呼叫,
- //重複呼叫對應異常資訊:This instance can only be executed once. Please instantiate a new instance.
- HelloWorldCommand helloWorldCommand = new HelloWorldCommand("Synchronous-hystrix");
- //使用execute()同步呼叫程式碼,效果等同於:helloWorldCommand.queue().get();
- String result = helloWorldCommand.execute();
- System.out.println("result=" + result);
- helloWorldCommand = new HelloWorldCommand("Asynchronous-hystrix");
- //非同步呼叫,可自由控制獲取結果時機,
- Future<String> future = helloWorldCommand.queue();
- //get操作不能超過command定義的超時時間,預設:1秒
- result = future.get(100, TimeUnit.MILLISECONDS);
- System.out.println("result=" + result);
- System.out.println("mainThread=" + Thread.currentThread().getName());
- }
- }
- //執行結果: run()方法在不同的執行緒下執行
- // result=Hello Synchronous-hystrix thread:hystrix-HelloWorldGroup-1
- // result=Hello Asynchronous-hystrix thread:hystrix-HelloWorldGroup-2
- // mainThread=main
note:非同步呼叫使用 command.queue()get(timeout, TimeUnit.MILLISECONDS);同步呼叫使用command.execute() 等同於 command.queue().get();
3:註冊非同步事件回撥執行
Java程式碼- //註冊觀察者事件攔截
- Observable<String> fs = new HelloWorldCommand("World").observe();
- //註冊結果回撥事件
- fs.subscribe(new Action1<String>() {
- @Override
- public void call(String result) {
- //執行結果處理,result 為HelloWorldCommand返回的結果
- //使用者對結果做二次處理.
- }
- });
- //註冊完整執行生命週期事件
- fs.subscribe(new Observer<String>() {
- @Override
- public void onCompleted() {
- // onNext/onError完成之後最後回撥
- System.out.println("execute onCompleted");
- }
- @Override
- public void onError(Throwable e) {
- // 當產生異常時回撥
- System.out.println("onError " + e.getMessage());
- e.printStackTrace();
- }
- @Override
- public void onNext(String v) {
- // 獲取結果後回撥
- System.out.println("onNext: " + v);
- }
- });
- /* 執行結果
- call execute result=Hello observe-hystrix thread:hystrix-HelloWorldGroup-3
- onNext: Hello observe-hystrix thread:hystrix-HelloWorldGroup-3
- execute onCompleted
- */
4:使用Fallback() 提供降級策略
Java程式碼
- //過載HystrixCommand 的getFallback方法實現邏輯
- public class HelloWorldCommand extends HystrixCommand<String> {
- private final String name;
- public HelloWorldCommand(String name) {
- super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))
- /* 配置依賴超時時間,500毫秒*/
- .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionIsolationThreadTimeoutInMilliseconds(500)));
- this.name = name;
- }
- @Override
- protected String getFallback() {
- return "exeucute Falled";
- }
- @Override
- protected String run() throws Exception {
- //sleep 1 秒,呼叫會超時
- TimeUnit.MILLISECONDS.sleep(1000);
- return "Hello " + name +" thread:" + Thread.currentThread().getName();
- }
- public static void main(String[] args) throws Exception{
- HelloWorldCommand command = new HelloWorldCommand("test-Fallback");
- String result = command.execute();
- }
- }
- /* 執行結果:getFallback() 呼叫執行
- getFallback executed
- */
NOTE: 除了HystrixBadRequestException異常之外,所有從run()方法丟擲的異常都算作失敗,並觸發降級getFallback()和斷路器邏輯。
HystrixBadRequestException用在非法引數或非系統故障異常等不應觸發回退邏輯的場景。
5:依賴命名:CommandKey
Java程式碼- public HelloWorldCommand(String name) {
- super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))