我學習,我驕傲,我爲國家省口罩。
–> 返回專欄總目錄 <–
代碼下載地址:https://github.com/f641385712/netflix-learning
目錄
前言
通過前面文章我們知道了,Hystrix是個強大的熔斷降級框架:收集目標方法的成功、失敗等指標信息,觸發熔斷器。其中失敗信息通過異常來表示,交給Hystrix進行統計。
但是,有的時候有些異常是並不能觸發熔斷的,比如請求參數異常等,那怎麼辦呢?或許你已經知道了結論:目標方法執行拋出異常時,除HystrixBadRequestException
之外,其他異常都會認爲是Hystrix命令執行失敗並觸發服務降級處理邏輯。那麼本文將深入研究爲何如此,以及給出實踐方案。
說明:閱讀本文之前建議你已經瞭解了Hystrix的回退機制,如上篇文章:三十一、Hystrix觸發fallback降級邏輯的5種情況及代碼示例
正文
通過上篇文章,我們已經瞭解到了Hystrix觸發fallback降級邏輯的5種情況,也就是:
- short-circuited短路
- threadpool-rejected線程池拒絕
- semaphore-rejected信號量拒絕
- timed-out超時
- failed執行失敗
出現這些類型的失敗均會觸發Hystrix的fallback機制。本文將介紹HystrixBadRequestException
這類型的異常將不會觸發fallabck機制。
認識HystrixBadRequestException
這個類本身並沒有什麼好說的,就是一個非常簡單的運行時異常:
public class HystrixBadRequestException extends RuntimeException {
private static final long serialVersionUID = -8341452103561805856L;
public HystrixBadRequestException(String message) {
super(message);
}
public HystrixBadRequestException(String message, Throwable cause) {
super(message, cause);
}
}
不過它的Javadoc對該類的作用描述:用提供的參數或狀態表示錯誤而不是執行失敗的異常。與HystrixCommand
拋出的所有其他異常不同,這不會觸發回退,不會計算故障指標,因此不會觸發斷路器。
注意:當一個錯誤是由於用戶輸入IllegalArgumentException
引起時(比如手誤),這個只應該使用,否則就會破壞容錯和回退行爲的目的。總的來說千萬別盲目使用,使用得最多的case是:結合Feign錯誤編碼器一起解決客戶端400異常而意外熔斷的問題~
熔斷器的數據從哪兒收集?
在解釋爲何不會觸發熔斷器之前,首先需要明白熔斷器的數據是從哪兒收集的?數據發射的源頭是哪兒?
在詳解HystrixCircuitBreaker
這篇文章的時候,我們知道它的健康指標數據來源於HealthCountsStream
這個數據流:統計時間窗口裏面各桶的值,彙總爲HealthCounts
對象輸出。
可以簡要複習下HealthCounts
這個類,它記錄着滑動窗口期間的請求數,包括:總數、失敗數、失敗百分比。它會統計如下事件:
HystrixEventType.SUCCESS
HystrixEventType.FAILURE
HystrixEventType.TIMEOUT
HystrixEventType.THREAD_POOL_REJECTED
HystrixEventType.SEMAPHORE_REJECTED
這些事件中除了1
,其它均爲失敗。另外2-4不就正好對應着文首寫着的觸發fallback的前四種情況嗎?
觸發fallback的情況和熔斷器事件類型的對應關係
下面繪製一張表格表達其對應關係:
失敗情況 | 原始異常類型 | 是否觸發fallback | 是否納入熔斷器統計 | 事件類型 |
---|---|---|---|---|
short-circuited短路 | RuntimeException | 是 | 是 | SHORT_CIRCUITED |
threadpool-rejected線程池拒絕 | RejectedExecutionException | 是 | 是 | THREAD_POOL_REJECTED |
semaphore-rejected信號量拒絕 | RuntimeException | 是 | 是 | SEMAPHORE_REJECTED |
timed-out超時 | TimeoutException | 是 | 是 | TIMEOUT |
failed失敗 | 目標方法拋出的異常類型 | 是 | 是 | FAILURE |
HystrixBadRequestException |
該異常亦由目標方法拋出 | 否 | 否 | 無 |
對此表格做如下幾點說明:
- 事件類型均爲
HystrixEventType
類型,本處前綴省略 - 這裏指的是原始異常類型,因爲最終經過fallback處理後都會被包爲
HystrixRuntimeException
- 實際上失敗情況
HystrixBadRequestException
和failed失敗
同屬目標方法拋出的異常,只是前者比較特殊而已~
結合熔斷器統計數據類HealthCounts
關心的幾個事件類型來說:除了HystrixBadRequestException
異常導致的失敗,其它均會被收集作爲斷路器的指標數據。
說明:
short-circuited短路
這種case就不用收集啦,因爲都已經短路了,就沒必要再收集了,否則斷路器永遠都自愈不回來就尷尬了
爲何不會觸發熔斷器?
所有的命令執行,最終在executeCommandAndObserve()
方法內:
AbstractCommand:
private Observable<R> executeCommandAndObserve(final AbstractCommand<R> _cmd) {
...
return execution.doOnNext(markEmits)
.doOnCompleted(markOnCompleted)
.onErrorResumeNext(handleFallback)
.doOnEach(setRequestContext);
}
其它部分本文不用關心,僅需關心onErrorResumeNext(handleFallback)
這個函數,它的觸發條件是:發射數據時(目標方法執行時)出現異常便會回調此函數,因此需要看看handleFallback
的邏輯。
說明:正常執行(成功)時不會回調此函數,而是回調的
doOnCompleted(markOnCompleted)
哦~
handleFallback
顧名思義,它是用於處理fallback的函數。
Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() {
// 當大聲異常時,回調此方法,該異常就是t
@Override
public Observable<R> call(Throwable t) {
// 若t就是Exception類型,那麼t和e一樣
// 若不是Exception類型,比如是Error類型。那就用Exception把它包起來
// new Exception("Throwable caught while executing.", t);
Exception e = getExceptionFromThrowable(t);
// 把異常寫進結果裏
executionResult = executionResult.setExecutionException(e);
// ==============針對不同異常的處理==============
if (e instanceof RejectedExecutionException) {
return handleThreadPoolRejectionViaFallback(e);
} else if (t instanceof HystrixTimeoutException) {
return handleTimeoutViaFallback();
} else if (t instanceof HystrixBadRequestException) {
return handleBadRequestByEmittingError(e);
} else {
return handleFailureViaFallback(e);
}
}
};
以上源碼,針對不同異常類型的處理方法,除了針對HystrixBadRequestException
異常類型沒講述過,其它均在上篇文章有過詳細闡述。下面具體看看handleBadRequestByEmittingError()
對該異常的處理。
handleBadRequestByEmittingError()
此方法專門用於處理HystrixBadRequestException
異常類型。
AbstractCommand:
private Observable<R> handleBadRequestByEmittingError(Exception underlying) {
Exception toEmit = underlying;
try {
long executionLatency = System.currentTimeMillis() - executionResult.getStartTimestamp();
// 請注意:這裏發送的是BAD_REQUEST事件哦~~~~
eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey);
executionResult = executionResult.addEvent((int) executionLatency, HystrixEventType.BAD_REQUEST);
// 留個鉤子:調用者可以對異常類型進行偷天換日
Exception decorated = executionHook.onError(this, FailureType.BAD_REQUEST_EXCEPTION, underlying);
// 如果調用者通過hook處理完後還是HystrixBadRequestException類型,那就直接把數據發射出去
// 若不是,那就不管,還是發射原來的異常類型
if (decorated instanceof HystrixBadRequestException) {
toEmit = decorated;
} else {
logger.warn("ExecutionHook.onError returned an exception that was not an instance of HystrixBadRequestException so will be ignored.", decorated);
}
} catch (Exception hookEx) {
logger.warn("Error calling HystrixCommandExecutionHook.onError", hookEx);
}
return Observable.error(toEmit);
}
這就是HystrixBadRequestException
的特殊對待邏輯,它發出的事件類型是HystrixEventType.BAD_REQUEST
,而此事件類型是不會被HealthCounts
作爲健康指標所統計的,因此它並不會觸發熔斷器。
使用場景
瞭解了HystrixBadRequestException
的這個特性後,使用場景可根據具體業務而定嘍。比如我們最爲常用的場景便是在Feign上自定義一個錯誤解碼器ErrorDecoder
,然後針對於錯誤碼是400的響應統一轉換爲HystrixBadRequestException
異常拋出,這樣是比較優雅的一種實踐方案。
總結
Hystrix拋出HystrixBadRequestException異常爲何不會觸發熔斷?這個話題就先聊到這了,到此篇爲止講述完了Hystrix執行時所有的異常狀態的處理方式。
小總結一下:Hystrix對異常HystrixBadRequestException
的處理髮送的事件類型HystrixEventType.BAD_REQUEST
,而該事件類型對負責給熔斷器收集指標數據的HealthCounts
是無效的,所以它並不會觸發熔斷器。
聲明
原創不易,碼字不易,多謝你的點贊、收藏、關注。把本文分享到你的朋友圈是被允許的,但拒絕抄襲
。你也可【左邊掃碼/或加wx:fsx641385712】邀請你加入我的 Java高工、架構師 系列羣大家庭學習和交流。
- [享學Netflix] 一、Apache Commons Configuration:你身邊的配置管理專家
- [享學Netflix] 二、Apache Commons Configuration事件監聽機制及使用ReloadingStrategy實現熱更新
- [享學Netflix] 三、Apache Commons Configuration2.x全新的事件-監聽機制
- [享學Netflix] 四、Apache Commons Configuration2.x文件定位系統FileLocator和FileHandler
- [享學Netflix] 五、Apache Commons Configuration2.x別樣的Builder模式:ConfigurationBuilder
- [享學Netflix] 六、Apache Commons Configuration2.x快速構建工具Parameters和Configurations
- [享學Netflix] 七、Apache Commons Configuration2.x如何實現文件熱加載/熱更新?
- [享學Netflix] 八、Apache Commons Configuration2.x相較於1.x使用上帶來哪些差異?
- [享學Netflix] 九、Archaius配置管理庫:初體驗及基礎API詳解
- [享學Netflix] 十、Archaius對Commons Configuration核心API Configuration的擴展實現
- [享學Netflix] 十一、Archaius配置管理器ConfigurationManager和動態屬性支持DynamicPropertySupport
- [享學Netflix] 十二、Archaius動態屬性DynamicProperty原理詳解(重要)
- [享學Netflix] 十三、Archaius屬性抽象Property和PropertyWrapper詳解
- [享學Netflix] 十四、Archaius如何對多環境、多區域、多雲部署提供配置支持?
- [享學Netflix] 十五、Archaius和Spring Cloud的集成:spring-cloud-starter-netflix-archaius
- [享學Netflix] 十六、Hystrix斷路器:初體驗及RxJava簡介
- [享學Netflix] 十七、Hystrix屬性抽象以及和Archaius整合實現配置外部化、動態化
- [享學Netflix] 十八、Hystrix配置之:全局配置和實例配置
- [享學Netflix] 十九、Hystrix插件機制:SPI接口介紹和HystrixPlugins詳解
- [享學Netflix] 二十、Hystrix跨線程傳遞數據解決方案:HystrixRequestContext
- [享學Netflix] 二十一、Hystrix指標數據收集(預熱):滑動窗口算法(附代碼示例)
- [享學Netflix] 二十二、Hystrix事件源與事件流:HystrixEvent和HystrixEventStream
- [享學Netflix] 二十三、Hystrix桶計數器:BucketedCounterStream
- [享學Netflix] 二十四、Hystrix在滑動窗口內統計:BucketedRollingCounterStream、HealthCountsStream
- [享學Netflix] 二十五、Hystrix累計統計流、分發流、最大併發流、配置流、功能流(附代碼示例)
- [享學Netflix] 二十六、Hystrix指標數據收集器:HystrixMetrics(HystrixDashboard的數據來源)
- [享學Netflix] 二十七、Hystrix何爲斷路器的半開狀態?HystrixCircuitBreaker詳解
- [享學Netflix] 二十八、Hystrix事件計數器EventCounts和執行結果ExecutionResult
- [享學Netflix] 二十九、Hystrix執行過程核心接口:HystrixExecutable、HystrixObservable和HystrixInvokableInfo
- [享學Netflix] 三十、Hystrix的fallback回退/降級邏輯源碼解讀:getFallbackOrThrowException
- [享學Netflix] 三十一、Hystrix觸發fallback降級邏輯的5種情況及代碼示例