【SpringCloud】Hystrix源碼解析

e246a1dda09849a5a89535a62441565d.png

hystrix是一個微服務容錯組件,提供了資源隔離、服務降級、服務熔斷的功能。這一章重點分析hystrix的實現原理

1、服務降級

CAP原則是分布式系統的一個理論基礎,它的三個關鍵屬性分別是一致性、可用性和容錯性。當服務實例所在服務器承受過大的壓力或者受到網絡因素影響沒法及時響應請求時,整個任務將處于阻塞狀態,這樣的系統容錯性不高,稍有不慎就會陷入癱瘓,hystrix為此提供了一種容錯機制:當服務實例沒法及時響應請求,可以采用服務降級的方式快速失敗,維持系統的穩定性

服務降級和@HystrixCommand注解綁定,查看它的源碼

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {...String fallbackMethod() default "";}

源碼提供的信息很少,想要分析注解的功能,還得找到處理注解信息的類:HystrixCommandAspect

@Aspect
public class HystrixCommandAspect {...// 環繞通知@Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()")public Object methodsAnnotatedWithHystrixCommand(ProceedingJoinPoint joinPoint) throws Throwable {Method method = AopUtils.getMethodFromTarget(joinPoint);Validate.notNull(method, "failed to get method from joinPoint: %s", new Object[]{joinPoint});if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) {throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser annotations at the same time");} else {MetaHolderFactory metaHolderFactory = (MetaHolderFactory)META_HOLDER_FACTORY_MAP.get(HystrixCommandAspect.HystrixPointcutType.of(method));MetaHolder metaHolder = metaHolderFactory.create(joinPoint);HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ? metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();try {Object result;if (!metaHolder.isObservable()) {// 代理執行方法result = CommandExecutor.execute(invokable, executionType, metaHolder);} else {result = this.executeObservable(invokable, executionType, metaHolder);}return result;} catch (HystrixBadRequestException var9) {throw var9.getCause();} catch (HystrixRuntimeException var10) {throw this.hystrixRuntimeExceptionToThrowable(metaHolder, var10);}}}
}

從命名上我們能看出這是一個切面,說明服務降級是通過aop代理實現的,跟蹤CommandExecutor的execute方法

調用鏈:
-> CommandExecutor.execute
-> castToExecutable(invokable, executionType).execute()
-> HystrixCommand.execute
-> this.queue().get()
public Future<R> queue() {// 獲取Future對象final Future<R> delegate = this.toObservable().toBlocking().toFuture();Future<R> f = new Future<R>() {...public R get() throws InterruptedException, ExecutionException {return delegate.get();}public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {return delegate.get(timeout, unit);}};...
}

HystrixCommand類的queue方法返回了一個Future對象,在線程任務中常用Future對象來獲取任務執行的結果。這里的Future對象是通過this.toObservable().toBlocking().toFuture()創建的,點擊查看toObservable方法,它返回一個Observable對象

public Observable<R> toObservable() {...final Func0<Observable<R>> applyHystrixSemantics = new Func0<Observable<R>>() {public Observable<R> call() {return 
((CommandState)AbstractCommand.this.commandState.get()).equals(AbstractCommand.CommandState.UNSUBSCRIBED) ? Observable.never() : // 傳入指令執行任務AbstractCommand.this.applyHystrixSemantics(AbstractCommand.this);}};...return Observable.defer(new Func0<Observable<R>>() {public Observable<R> call() {...// 有訂閱者訂閱了才創建Observable對象Observable<R> hystrixObservable = Observable.defer(applyHystrixSemantics).map(wrapWithAllOnNextHooks);Observable afterCache;if (requestCacheEnabled && cacheKey != null) {HystrixCachedObservable<R> toCache = HystrixCachedObservable.from(hystrixObservable, AbstractCommand.this);HystrixCommandResponseFromCache<R> fromCache = (HystrixCommandResponseFromCache)AbstractCommand.this.requestCache.putIfAbsent(cacheKey, toCache);if (fromCache != null) {toCache.unsubscribe();AbstractCommand.this.isResponseFromCache = true;return AbstractCommand.this.handleRequestCacheHitAndEmitValues(fromCache, AbstractCommand.this);}afterCache = toCache.toObservable();} else {afterCache = hystrixObservable;}return afterCache.doOnTerminate(terminateCommandCleanup).doOnUnsubscribe(unsubscribeCommandCleanup).doOnCompleted(fireOnCompletedHook);...}});        
}

Observable對象的創建任務委托了給了AbstractCommand.this.applyHystrixSemantics方法

private Observable<R> applyHystrixSemantics(AbstractCommand<R> _cmd) {this.executionHook.onStart(_cmd);// 是否允許請求,判斷熔斷狀態if (this.circuitBreaker.allowRequest()) {final TryableSemaphore executionSemaphore = this.getExecutionSemaphore();final AtomicBoolean semaphoreHasBeenReleased = new AtomicBoolean(false);Action0 singleSemaphoreRelease = new Action0() {public void call() {if (semaphoreHasBeenReleased.compareAndSet(false, true)) {executionSemaphore.release();}}};Action1<Throwable> markExceptionThrown = new Action1<Throwable>() {public void call(Throwable t) {AbstractCommand.this.eventNotifier.markEvent(HystrixEventType.EXCEPTION_THROWN, AbstractCommand.this.commandKey);}};if (executionSemaphore.tryAcquire()) {try {this.executionResult = this.executionResult.setInvocationStartTime(System.currentTimeMillis());// 執行任務return this.executeCommandAndObserve(_cmd).doOnError(markExceptionThrown).doOnTerminate(singleSemaphoreRelease).doOnUnsubscribe(singleSemaphoreRelease);} catch (RuntimeException var7) {return Observable.error(var7);}} else {return this.handleSemaphoreRejectionViaFallback();}} else {// 處于熔斷狀態,執行備用任務return this.handleShortCircuitViaFallback();}
}

this.circuitBreaker.allowReques返回true表示沒有熔斷,走executeCommandAndObserve方法

private Observable<R> executeCommandAndObserve(AbstractCommand<R> _cmd) {...Observable execution;if ((Boolean)this.properties.executionTimeoutEnabled().get()) {// 添加了超時監控execution = this.executeCommandWithSpecifiedIsolation(_cmd).lift(new HystrixObservableTimeoutOperator(_cmd));} else {execution = this.executeCommandWithSpecifiedIsolation(_cmd);}...// handleFallback:不同異常狀況下使用不同的處理方法Func1<Throwable, Observable<R>> handleFallback = new Func1<Throwable, Observable<R>>() {public Observable<R> call(Throwable t) {Exception e = AbstractCommand.this.getExceptionFromThrowable(t);AbstractCommand.this.executionResult = AbstractCommand.this.executionResult.setExecutionException(e);if (e instanceof RejectedExecutionException) {return AbstractCommand.this.handleThreadPoolRejectionViaFallback(e);} else if (t instanceof HystrixTimeoutException) {// 拋出超時異常時,做超時處理return AbstractCommand.this.handleTimeoutViaFallback();} else if (t instanceof HystrixBadRequestException) {return AbstractCommand.this.handleBadRequestByEmittingError(e);} else if (e instanceof HystrixBadRequestException) {AbstractCommand.this.eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, AbstractCommand.this.commandKey);return Observable.error(e);} else {return AbstractCommand.this.handleFailureViaFallback(e);}}};...return execution.doOnNext(markEmits).doOnCompleted(markOnCompleted)// 調用handleFallback處理異常.onErrorResumeNext(handleFallback).doOnEach(setRequestContext);
}
private static class HystrixObservableTimeoutOperator<R> implements Observable.Operator<R, R> {final AbstractCommand<R> originalCommand;public HystrixObservableTimeoutOperator(AbstractCommand<R> originalCommand) {this.originalCommand = originalCommand;}public Subscriber<? super R> call(final Subscriber<? super R> child) {final CompositeSubscription s = new CompositeSubscription();child.add(s);final HystrixContextRunnable timeoutRunnable = new HystrixContextRunnable(this.originalCommand.concurrencyStrategy, new Runnable() {public void run() {// 3.拋出超時異常child.onError(new HystrixTimeoutException());}});HystrixTimer.TimerListener listener = new HystrixTimer.TimerListener() {// 1.判斷是否超時public void tick() {if (HystrixObservableTimeoutOperator.this.originalCommand.isCommandTimedOut.compareAndSet(AbstractCommand.TimedOutStatus.NOT_EXECUTED, AbstractCommand.TimedOutStatus.TIMED_OUT)) {HystrixObservableTimeoutOperator.this.originalCommand.eventNotifier.markEvent(HystrixEventType.TIMEOUT, HystrixObservableTimeoutOperator.this.originalCommand.commandKey);s.unsubscribe();// 2.執行超時任務timeoutRunnable.run();}}};}}

executeCommandAndObserve方法添加超時監控,如果任務執行超出限制時間會拋出超時異常,由handleTimeoutViaFallback方法處理異常

private Observable<R> handleTimeoutViaFallback() {// 1.根據異常類型處理異常return this.getFallbackOrThrowException(this, HystrixEventType.TIMEOUT, FailureType.TIMEOUT, "timed-out", new TimeoutException());
}private Observable<R> getFallbackOrThrowException(final AbstractCommand<R> _cmd, HystrixEventType eventType, final HystrixRuntimeException.FailureType failureType, final String message, final Exception originalException) {...// 獲取回調觀察者fallbackExecutionChain = this.getFallbackObservable();...
}    protected final Observable<R> getFallbackObservable() {return Observable.defer(new Func0<Observable<R>>() {public Observable<R> call() {try {// 執行備用方法return Observable.just(HystrixCommand.this.getFallback());} catch (Throwable var2) {return Observable.error(var2);}}});
}

到這里終于看到了getFallback方法,它會調用注解中fallback指向的方法,快速失敗返回響應結果

protected Object getFallback() {// 獲取注解中的備用方法信息final CommandAction commandAction = this.getFallbackAction();if (commandAction != null) {try {return this.process(new AbstractHystrixCommand<Object>.Action() {Object execute() {MetaHolder metaHolder = commandAction.getMetaHolder();Object[] args = CommonUtils.createArgsForFallback(metaHolder, GenericCommand.this.getExecutionException());return commandAction.executeWithArgs(metaHolder.getFallbackExecutionType(), args);}});} catch (Throwable var3) {LOGGER.error(FallbackErrorMessageBuilder.create().append(commandAction, var3).build());throw new FallbackInvocationException(ExceptionUtils.unwrapCause(var3));}} else {return super.getFallback();}
}

回到AbstractCommand.this.applyHystrixSemantics方法,當this.circuitBreaker.allowReques返回true是請求正常往下走,當它返回false時表示服務進入熔斷狀態,會走else分支,同樣會進入getFallback方法

調用鏈
-> AbstractCommand.handleShortCircuitViaFallback
-> getFallbackOrThrowException
-> this.getFallbackObservable
-> GenericCommand.getFallback

2、服務熔斷

服務熔斷是hystrix提供的一種保護機制,當一段時間內服務響應的異常的次數過多,hystrix會讓服務降級快速返回失敗信息,避免累積壓力造成服務崩潰。
聯系上文找到circuitBreaker.allowRequest方法,該方法判斷是否允許請求往下走

public boolean allowRequest() {// 是否強制打開熔斷if ((Boolean)this.properties.circuitBreakerForceOpen().get()) {return false;// 是否強制關閉熔斷} else if ((Boolean)this.properties.circuitBreakerForceClosed().get()) {this.isOpen();return true;} else {return !this.isOpen() || this.allowSingleTest();}
}public boolean isOpen() {if (this.circuitOpen.get()) {return true;} else {HystrixCommandMetrics.HealthCounts health = this.metrics.getHealthCounts();// 請求次數是否超過單位時間內請求數閾值if (health.getTotalRequests() < (long)(Integer)this.properties.circuitBreakerRequestVolumeThreshold().get()) {return false;// 請求異常次數占比} else if (health.getErrorPercentage() < (Integer)this.properties.circuitBreakerErrorThresholdPercentage().get()) {return false;} else if (this.circuitOpen.compareAndSet(false, true)) {this.circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());return true;} else {return true;}}
}

isOpen方法內有針對請求的各種量化計算,當請求異常情況過多,就會觸發熔斷,走服務降級

3、總結

hystrix組件會根據請求狀態判斷是否執行請求,當請求超時或者存在其他異常會走備用方法,當異常次數過多會進入熔斷狀態快速失敗,避免服務累積過多壓力

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/37455.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/37455.shtml
英文地址,請注明出處:http://en.pswp.cn/web/37455.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

c++【入門】挖胡蘿卜

限制 時間限制 : 1 秒 內存限制 : 128 MB 題目 小兔朱迪挖了x個胡蘿卜&#xff0c;狐貍尼克挖到胡蘿卜數量是小兔挖到的3倍&#xff0c;小羊肖恩挖到胡蘿卜的數量比狐貍尼克少8個&#xff1b; 請你編程計算一下狐貍尼克和小羊肖恩分別挖了幾個胡蘿卜&#xff0c;以及平均每…

前端工程化09-webpack靜態的模塊化打包工具(未完結)

9.1、開發模式的進化歷史 webpacks是一個非常非常的強大的一個工具&#xff0c;相應的這個東西的學習也是有一定的難度的&#xff0c;里邊的東西非常的多&#xff0c;里面涉及到的 概念的話也是非常非常的多的。 這個東西既然非常重要&#xff0c;那么在我們前端到底處于怎樣…

HCIA4.26-5.10

OSPF ——開放式最短路徑優先協議 無類別鏈路狀態IGP動態路由協議 距離矢量協議 運行距離矢量協議的路由器會周期性的泛洪自己的路由表&#xff0c;通過路由之間的交互&#xff0c;每臺路由器都從相鄰的路由器學習到路由條目&#xff0c;隨后加載進自己的路由表中。對于網絡…

GD32 開發筆記

0x01 GPIO時鐘使能的坑 使用GD32的GPIO引腳來控制 74HC595 &#xff0c;發現引腳一直無法控制&#xff0c;始終輸出3.3v&#xff0c;初始化環節應該是出了問題。用通俗的話來說&#xff0c;就是點燈點不亮 排查了MCU、光耦隔離芯片、被強行上拉等問題&#xff0c;最后發現是G…

Python代碼分析和修復工具庫之coala使用詳解

概要 代碼質量在軟件開發中至關重要,保持代碼的可讀性、一致性和易維護性是每個開發者的目標。coala 是一個開源的代碼分析和修復工具,旨在幫助開發者自動化代碼質量檢查,支持多種編程語言,包括 Python、C++、JavaScript 等。通過使用 coala,開發者可以方便地集成代碼檢查…

AI時代的軟件工程:挑戰與改變

人工智能&#xff08;AI&#xff09;正以驚人的速度改變著我們的生活和工作方式。作為與AI關系最為密切的領域之一&#xff0c;軟件工程正經歷著深刻的轉變。 1 軟件工程的演變 軟件工程的起源 軟件工程&#xff08;Software Engineering&#xff09;是關于如何系統化、規范化地…

input調用手機攝像頭實現拍照功能vue

項目需要一個拍照功能&#xff0c;實現功能如下圖所示:若使用瀏覽器則可以直接上傳圖片&#xff0c;若使用手機則調用手機攝像頭拍照。 1.代碼結構 <!--input標簽--> <input ref"photoRef"type"file"accept"image/*"capture"envir…

Leetcode 3202. Find the Maximum Length of Valid Subsequence II

Leetcode 3202. Find the Maximum Length of Valid Subsequence II 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;3202. Find the Maximum Length of Valid Subsequence II 1. 解題思路 這一題的話是上一題3201. Find the Maximum Length of Valid Subsequence I的升級版&am…

基于多源數據的密碼攻防領域知識圖譜構建

源自&#xff1a; 信息安全與通信保密雜志社 作者&#xff1a;曹增輝 , 郭淵博 , 黃慧敏 摘 要 提高網絡空間安全的密碼攻防能力&#xff0c;需要形成可表示、可共享、可分析的領域知識模式和知識庫。利用自頂向下的構建方法&#xff0c;并通過本體構建方法梳理密碼攻防領域…

IPSec:互聯網協議安全機制的深度解析與應用

目錄 一、IPSec概述 二、IPSec的組成 三、IPSec的工作原理 四、IPSec的用途 IPSec&#xff08;Internet Protocol Security&#xff09;作為現代網絡通信中不可或缺的安全基礎設施&#xff0c;旨在為基于IP&#xff08;Internet Protocol&#xff09;的數據傳輸提供端到端的…

MySQL數據庫鎖詳解

MySQL數據庫鎖詳解 在多用戶環境下&#xff0c;數據庫鎖用于保證事務的完整性和數據的一致性。MySQL提供了多種不同類型的鎖&#xff0c;以適應不同的并發需求和性能考慮。本文將詳細介紹MySQL中的鎖機制&#xff0c;包括鎖的類型、鎖定機制的原理以及如何管理鎖。 1. 鎖的類…

【Linux】虛擬機安裝openEuler 24.03 X86_64 教程

目錄 一、概述 1.1 openEuler 覆蓋全場景的創新平臺 1.2 系統框架 1.3 平臺框架 二、安裝詳細步驟 一、概述 1.1 openEuler 覆蓋全場景的創新平臺 openEuler 已支持 x86、Arm、SW64、RISC-V、LoongArch 多處理器架構&#xff0c;逐步擴展 PowerPC 等更多芯片架構支持&…

時間序列季節性和周期性

季節性 (Seasonality) 定義 季節性是指時間序列數據中由于自然、社會或經濟因素&#xff0c;在固定且短期的時間間隔內&#xff08;如每年、每季度、每月或每周&#xff09;重復出現的模式或波動。 特點 固定周期&#xff1a;季節性波動有一個固定的周期。例如&#xff0c;…

【小工具】 Unity相機寬度適配

相機默認是根據高度適配的&#xff0c;但是在部分游戲中需要根據寬度進行適配 實現步驟 定義標準屏幕寬、高判斷標準屏幕寬高比與當前的是否相等通過**&#xff08;標準寬度/當前寬度&#xff09; &#xff08;標準高度 / 當前高度&#xff09;**計算縮放調整相機fieldOfView即…

iptables 防火墻(一)

iptables 防火墻&#xff08;一&#xff09; 一、Linux 防火墻基礎防火墻分類 二、iptables 的表、鏈結構規則表規則鏈數據包過濾的匹配流程 三、編寫防火墻規則iptables 的安裝iptables的基本語法規則的匹配條件通用匹配隱含匹配顯式匹配 四、總結 在網絡安全的世界里&#xf…

XRP對接文檔

XRP對接文檔 技術預研 參考文檔 官方文檔: https://xrpl.org/list-xrp-in-your-exchange.html 官方文檔: https://xrpl.org/list-xrp-as-an-exchange.html#flow-of-funds 交易所對接XRP(內容齊全, 很推薦) https://blog.csdn.net/weixin_40396076/article/details/10020207…

基于51單片機的籃球計時器Proteus仿真

文章目錄 一、籃球計時器1.題目要求2.思路3.仿真圖3.1 未仿真時3.2 仿真開始3.3 A隊進分3.4 B隊進分3.5 比賽結束 4.仿真程序4.1 主函數4.2 時間顯示4.3 比分顯示4.4 按鍵掃描 二、總結 一、籃球計時器 1.題目要求 以51單片機為核心&#xff0c;設計并制作籃球計時器 基本功…

代碼托管平臺詳解與比較

1. Gitee 1.1 平臺簡介 Gitee是中國本土的一個代碼托管平臺&#xff0c;類似于GitHub。它提供了版本控制、項目管理和代碼托管等服務&#xff0c;特別適合中國的開發者和企業。 1.2 主要特點 1. 本地化支持&#xff1a;Gitee提供中文界面和文檔&#xff0c;適合中國開發者使用…

電子游戲 - 星際爭霸技術提高

提高《星際爭霸》的勝率需要綜合考慮多方面的因素&#xff0c;包括宏觀管理、微操技巧、策略制定和游戲意識。以下是一些具體的建議&#xff0c;可以幫助你打好《星際爭霸》并提高勝率&#xff1a; 1. 加強資源管理和經濟發展&#xff08;Macro&#xff09; * 快速擴張&#x…

python實現符文加、解密

在歷史悠久的加密技術中&#xff0c;愷撒密碼以其簡單卻有效的原理聞名。通過固定的字母位移&#xff0c;明文可以被轉換成密文&#xff0c;而解密則是逆向操作。這種技術不僅適用于英文字母&#xff0c;還可以擴展到其他語言的字符體系&#xff0c;如日語的平假名或漢語的拼音…