雪崩效應
現如今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