1. 程式人生 > >Hystrix 服務降級與監控

Hystrix 服務降級與監控

一:為什麼需要Hystrix?

在大中型分散式系統中,通常系統很多依賴(HTTP,hession,Netty,Dubbo等),如下圖:

在高併發訪問下,這些依賴的穩定性與否對系統的影響非常大,但是依賴有很多不可控問題:如網路連線緩慢,資源繁忙,暫時不可用,服務離線等.

如下圖:QPS為50的依賴 I 出現不可用,但是其他依賴仍然可用.

當依賴I 阻塞時,大多數伺服器的執行緒池就出現阻塞(BLOCK),影響整個線上服務的穩定性.如下圖:

在複雜的分散式架構的應用程式有很多的依賴,都會不可避免地在某些時候失敗。高併發的依賴失敗時如果沒有隔離措施,當前應用服務就有被拖垮的風險。

Java程式碼  收藏程式碼
  1. 例如:一個依賴30個SOA服務的系統,每個服務99.99%可用。  
  2. 99.99%的30次方 ≈ 99.7%  
  3. 0.3% 意味著一億次請求 會有 3,000,00次失敗  
  4. 換算成時間大約每月有2個小時服務不穩定.  
  5. 隨著服務依賴數量的變多,服務不穩定的概率會成指數性提高.  

 解決問題方案:對依賴做隔離,Hystrix就是處理依賴隔離的框架,同時也是可以幫我們做依賴服務的治理和監控.

Netflix 公司開發併成功使用Hystrix,使用規模如下:

Java程式碼  收藏程式碼
  1. The Netflix API processes 10
    + billion HystrixCommand executions per day using thread isolation.   
  2. 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程式碼  收藏程式碼
  1. <!-- 依賴版本 -->  
  2. <hystrix.version>1.3.16</hystrix.version>  
  3. <hystrix-metrics-event-stream.version>1.1.2</hystrix-metrics-event-stream.version>   
  4. <dependency>  
  5.      <groupId>com.netflix.hystrix</groupId>  
  6.      <artifactId>hystrix-core</artifactId>  
  7.      <version>${hystrix.version}</version>  
  8.  </dependency>  
  9.      <dependency>  
  10.      <groupId>com.netflix.hystrix</groupId>  
  11.      <artifactId>hystrix-metrics-event-stream</artifactId>  
  12.      <version>${hystrix-metrics-event-stream.version}</version>  
  13.  </dependency>  
  14. <!-- 倉庫地址 -->  
  15. <repository>  
  16.      <id>nexus</id>  
  17.      <name>local private nexus</name>  
  18.      <url>http://maven.oschina.net/content/groups/public/</url>  
  19.      <releases>  
  20.           <enabled>true</enabled>  
  21.      </releases>  
  22.      <snapshots>  
  23.           <enabled>false</enabled>  
  24.      </snapshots>  
  25. </repository>  

2:使用命令模式封裝依賴邏輯

Java程式碼  收藏程式碼
  1. public class HelloWorldCommand extends HystrixCommand<String> {  
  2.     private final String name;  
  3.     public HelloWorldCommand(String name) {  
  4.         //最少配置:指定命令組名(CommandGroup)  
  5.         super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));  
  6.         this.name = name;  
  7.     }  
  8.     @Override  
  9.     protected String run() {  
  10.         // 依賴邏輯封裝在run()方法中  
  11.         return "Hello " + name +" thread:" + Thread.currentThread().getName();  
  12.     }  
  13.     //呼叫例項  
  14.     public static void main(String[] args) throws Exception{  
  15.         //每個Command物件只能呼叫一次,不可以重複呼叫,  
  16.         //重複呼叫對應異常資訊:This instance can only be executed once. Please instantiate a new instance.  
  17.         HelloWorldCommand helloWorldCommand = new HelloWorldCommand("Synchronous-hystrix");  
  18.         //使用execute()同步呼叫程式碼,效果等同於:helloWorldCommand.queue().get();   
  19.         String result = helloWorldCommand.execute();  
  20.         System.out.println("result=" + result);  
  21.         helloWorldCommand = new HelloWorldCommand("Asynchronous-hystrix");  
  22.         //非同步呼叫,可自由控制獲取結果時機,  
  23.         Future<String> future = helloWorldCommand.queue();  
  24.         //get操作不能超過command定義的超時時間,預設:1秒  
  25.         result = future.get(100, TimeUnit.MILLISECONDS);  
  26.         System.out.println("result=" + result);  
  27.         System.out.println("mainThread=" + Thread.currentThread().getName());  
  28.     }  
  29. }  
  30.     //執行結果: run()方法在不同的執行緒下執行  
  31.     // result=Hello Synchronous-hystrix thread:hystrix-HelloWorldGroup-1  
  32.     // result=Hello Asynchronous-hystrix thread:hystrix-HelloWorldGroup-2  
  33.     // mainThread=main  

 note:非同步呼叫使用 command.queue()get(timeout, TimeUnit.MILLISECONDS);同步呼叫使用command.execute() 等同於 command.queue().get();

3:註冊非同步事件回撥執行

Java程式碼  收藏程式碼
  1. //註冊觀察者事件攔截  
  2. Observable<String> fs = new HelloWorldCommand("World").observe();  
  3. //註冊結果回撥事件  
  4. fs.subscribe(new Action1<String>() {  
  5.     @Override  
  6.     public void call(String result) {  
  7.          //執行結果處理,result 為HelloWorldCommand返回的結果  
  8.         //使用者對結果做二次處理.  
  9.     }  
  10. });  
  11. //註冊完整執行生命週期事件  
  12. fs.subscribe(new Observer<String>() {  
  13.             @Override  
  14.             public void onCompleted() {  
  15.                 // onNext/onError完成之後最後回撥  
  16.                 System.out.println("execute onCompleted");  
  17.             }  
  18.             @Override  
  19.             public void onError(Throwable e) {  
  20.                 // 當產生異常時回撥  
  21.                 System.out.println("onError " + e.getMessage());  
  22.                 e.printStackTrace();  
  23.             }  
  24.             @Override  
  25.             public void onNext(String v) {  
  26.                 // 獲取結果後回撥  
  27.                 System.out.println("onNext: " + v);  
  28.             }  
  29.         });  
  30. /* 執行結果 
  31. call execute result=Hello observe-hystrix thread:hystrix-HelloWorldGroup-3 
  32. onNext: Hello observe-hystrix thread:hystrix-HelloWorldGroup-3 
  33. execute onCompleted 
  34. */  

4:使用Fallback() 提供降級策略

Java程式碼  收藏程式碼
  1. //過載HystrixCommand 的getFallback方法實現邏輯  
  2. public class HelloWorldCommand extends HystrixCommand<String> {  
  3.     private final String name;  
  4.     public HelloWorldCommand(String name) {  
  5.         super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))  
  6.                 /* 配置依賴超時時間,500毫秒*/  
  7.                 .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionIsolationThreadTimeoutInMilliseconds(500)));  
  8.         this.name = name;  
  9.     }  
  10.     @Override  
  11.     protected String getFallback() {  
  12.         return "exeucute Falled";  
  13.     }  
  14.     @Override  
  15.     protected String run() throws Exception {  
  16.         //sleep 1 秒,呼叫會超時  
  17.         TimeUnit.MILLISECONDS.sleep(1000);  
  18.         return "Hello " + name +" thread:" + Thread.currentThread().getName();  
  19.     }  
  20.     public static void main(String[] args) throws Exception{  
  21.         HelloWorldCommand command = new HelloWorldCommand("test-Fallback");  
  22.         String result = command.execute();  
  23.     }  
  24. }  
  25. /* 執行結果:getFallback() 呼叫執行 
  26. getFallback executed 
  27. */  

NOTE: 除了HystrixBadRequestException異常之外,所有從run()方法丟擲的異常都算作失敗,並觸發降級getFallback()和斷路器邏輯。

          HystrixBadRequestException用在非法引數或非系統故障異常等不應觸發回退邏輯的場景。

5:依賴命名:CommandKey

Java程式碼  收藏程式碼
  1. public HelloWorldCommand(String name) {  
  2.         super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))  
  3.