Spring Cloud 原始碼學習之 Hystrix 入門
歡迎訪問陳同學部落格原文
Hystrix 功能非常多,本文僅對 Hystrix 原始碼做入門學習。為便於閱讀,文中原始碼有較大刪減,僅保留入門學習必要的原始碼,降低其他邏輯的干擾。
從 Hystrix 名字說起
Spring Cloud 眾多元件,瞭解其名字背後的寓意也是一種樂趣。
下面是我拼的一張圖,分別為:Hystrix、豪豬、刺蝟。
Hystrix 譯為 "豪豬",豪豬以棘刺聞名,集肉用、藥用、欣賞價值於一體。刺蝟的小短刺和豪豬長矛比起來,根本不在同一個level。超市中70塊一斤的豬肉指不定就是豪豬,當然,也可能是丁磊家的黑豬。
豪豬的棘刺能保護自己不受天敵傷害,代表了強大的防禦能力。Netflix 將該元件取名為 Hystrix,宣言為 "defend your app",寓意應該是:當系統受到傷害時,能夠像豪豬的棘刺一樣保護系統。
Spring Cloud Hystrix 基於 Netflix Hystrix 實現,具備服務降級、服務熔斷、執行緒與訊號隔離、請求快取、請求合併以及服務監控等強大功能。
入門學習素材
本文使用下面的樣例程式碼來做原始碼學習。
ServiceA 中 hello() 方法由 @HystrixCommand 註解標記,呼叫 ServiceB 的 hello() 介面。若呼叫失敗,則執行 error() 方法。
@HystrixCommand(fallbackMethod = "error")
public String hello() {
return restTemplate.getForEntity("http://serviceB/hello", String.class).getBody();
}
public String error() {
return "error";
}
ServiceB hello() 丟擲異常,以便 ServiceA執行 error() 方法。
@GetMapping("/hello")
public String hello() {
throw new RuntimeException("error occurred");
}
樣例程式碼表示的就是 服務降級,服務降級換些名詞來描述就是:B計劃、應急預案、備用方案、替補,以便在出現問題時,"預備隊"可以立馬頂上。
有時,技術名詞晦澀難懂,但經驗與智慧都來自於現實世界。
程式碼執行入口
Spring 中也有一種類似 Java SPI 的載入機制,允許在 META-INF/spring.factories 檔案中配置介面實現類,Spring 會自動處理。開發人員僅需引入 jar 包,就能達到插拔式效果,十分方便。
引入 spring-cloud-starter-hystrix 依賴,spring-cloud-netflix-core 的 jar 包中包含 spring.factories 檔案,其中有 Hytrix 和 其他元件相關配置。
在 HystrixCircuitBreakerConfiguration 中,注入了 HystrixCommandAspect。
@Bean
public HystrixCommandAspect hystrixCommandAspect() {
return new HystrixCommandAspect();
}
HystrixCommandAspect 用於處理被註解 @HystrixCommand 標記的方法。通過名字和下面程式碼可以知道,Hystrix 基於 AOP 機制實現,對目標方法做了代理,然後實現了自己一系列功能特性。
@Aspect
public class HystrixCommandAspect {
@Pointcut("@annotation(...annotation.HystrixCommand)")
public void hystrixCommandAnnotationPointcut() {
}
@Around("hystrixCommandAnnotationPointcut()")
public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
}
}
處理邏輯就在 methodsAnnotatedWithHystrixCommand() 中。
處理邏輯
methodsAnnotatedWithHystrixCommand() 用來執行目標方法,Hystrix 將需要執行的Method(如ServiceA的hello() ) 最終封裝成了 HystrixInvokable 來執行。
public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
// 被@HystrixCommand標記的hello()方法
Method method = getMethodFromTarget(joinPoint);
MetaHolderFactory metaHolderFactory = ...get(HystrixPointcutType.of(method));
MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
// 準備各種材料後,建立HystrixInvokable
HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
Object result;
try {
if (!metaHolder.isObservable()) {
// 利用工具CommandExecutor來執行
result = CommandExecutor.execute(invokable, executionType, metaHolder);
}
}
return result;
}
HystrixInvokable 只是一個空介面,沒有任何方法,只是用來標記具備可執行的能力。
那 HystrixInvokable 又是如何建立的?它具體的實現類又是什麼?先看看 HystrixCommandFactory.getInstance().create() 的程式碼。
public HystrixInvokable create(MetaHolder metaHolder) {
return new GenericCommand(...create(metaHolder));
}
實現類是 GenericCommand,我們看看類圖。
三個抽象父類 AbstractHystrixCommand、HystrixCommand、AbstractCommand 幫助 GenericCommand 做了不少公共的事情,而 GenericCommand 負責執行具體的方法和fallback時的方法。
// 執行具體的方法,如:ServiceA的hello()
protected Object run() throws Exception {
return process(new Action() {
@Override
Object execute() {
return getCommandAction().execute(getExecutionType());
}
});
}
// 執行fallback方法,如:ServiceA的error()
protected Object getFallback() {
final CommandAction commandAction = getFallbackAction();
return process(new Action() {
@Override
Object execute() {
MetaHolder metaHolder = commandAction.getMetaHolder();
Object[] args = createArgsForFallback(...);
return commandAction.executeWithArgs(..., args);
}
});
}
目標方法執行細節
執行過程想來應該很簡單,即先執行目標方法,失敗則執行fallback方法。
再來看看 methodsAnnotatedWithHystrixCommand() 的具體執行程式碼,它完成了 Hystrix 的整個執行過程。
Object result = CommandExecutor.execute(invokable, executionType, metaHolder);
CommandExecutor.execute() 首先將 invokable 轉換為 HystrixExecutable,再執行 HystrixExecutable 的execute() 方法。
public static Object execute(HystrixInvokable invokable, ExecutionType executionType, MetaHolder metaHolder) throws RuntimeException {
switch (executionType) {
// 這裡僅貼出這一種case
case SYNCHRONOUS: {
// 轉為 HystrixExecutable 並執行
return castToExecutable(invokable, executionType).execute();
}
}
}
HystrixExecutable 的 execute() 方法由 HystrixCommand.execute() 實現,程式碼如下:
public R execute() {
// 呼叫下面的queue()
return queue().get();
}
public Future<R> queue() {
final Future<R> delegate = toObservable().toBlocking().toFuture();
final Future<R> f = new Future<R>() { ... };
if (f.isDone()) {
try {
f.get();
return f;
}
}
return f;
}
利用 JUC 的 Future 來非同步執行,通過 f.get() 來獲取 hello() 方法的執行結果。Hystrix 結合了 RxJava 來實現非同步程式設計,我做了下除錯,看了stackframe,執行過程層層呼叫,略微噁心。RxJava 有點複雜,同時也需要了解響應式程式設計模型,這裡直接跳過。
ServiceA 的 hello() 還是由 GenericCommand 來執行的,如下圖,getCommandAction() 這個 CommandAction 指的就是被執行的hello()方法,利用Java反射機制來執行。
上圖右邊部分標記出來的就是 RxJava 中的部分呼叫鏈,下面的截圖簡單展示下最後的呼叫。
OnSubscribeDefer.call() -> HystrixCommand.getExecutionObservable() -> GenericCommand.run()。
小結
本文只是一個簡單小例子,沒有涉及到 Hystrix 的其他特性,後面將接著學習。另,Hystrix 的官方 Wiki 是非常好的學習材料。
歡迎關注陳同學的公眾號,一起學習,一起成長
相關文章
- Spring Cloud 之 Hystrix.SpringCloud
- springcloud學習筆記(四)Spring Cloud HystrixSpringGCCloud筆記
- SpringCloud原始碼學習之Hystrix熔斷器SpringGCCloud原始碼
- Spring Cloud Hystrix原碼篇(十一)SpringCloud
- spring cloud之斷路器hystrix(五)SpringCloud
- 7、Spring Cloud HystrixSpringCloud
- Spring原始碼學習之:ClassLoader學習(2)Spring原始碼
- Spring原始碼學習之:ClassLoader學習(3)Spring原始碼
- Spring原始碼學習之:ClassLoader學習(4)Spring原始碼
- MySQL入門學習之——原始碼安裝mysql5.5MySql原始碼
- springCloud入門學習--Hystrix狀態監控SpringGCCloud
- springMvc原始碼學習之:spring原始碼總結SpringMVC原始碼
- Spring Cloud 原始碼分析之OpenFeignSpringCloud原始碼
- spring入門學習Spring
- Spring原始碼學習之:spring註解@TransactionalSpring原始碼
- Spring Cloud入門教程-Hystrix斷路器實現容錯和降級SpringCloud
- Spring-cloud學習筆記--- Eureka原始碼剖析之服務註冊介面SpringCloud筆記原始碼
- 【spring原始碼學習】spring的IOC容器之BeanFactoryPostProcessor介面學習Spring原始碼Bean
- 【spring原始碼學習】Spring的IOC容器之BeanPostProcessor介面學習Spring原始碼Bean
- Spring學習筆記之Spring MVC 入門教程Spring筆記MVC
- 原始碼學習之Spring容器建立原理原始碼Spring
- Spring原始碼學習之:FactoryBean的使用Spring原始碼Bean
- Spring Cloud 關於:Spring Cloud Netflix HystrixSpringCloud
- Spring cloud(4)-熔斷(Hystrix)SpringCloud
- Spring Cloud Gateway 入門SpringCloudGateway
- Spring原始碼學習之:ClassLoader學習(5)-自測Spring原始碼
- Spring-cloud學習筆記---Ribbon原始碼剖析之攔截器Interceptor方法SpringCloud筆記原始碼
- Spring學習之——手寫Mini版Spring原始碼Spring原始碼
- 學習Spring原始碼篇之環境搭建Spring原始碼
- Spring Cloud學習SpringCloud
- Spring Cloud Hystrix應用篇(十一)SpringCloud
- Spring Cloud Hystrix 容錯保護SpringCloud
- Spring Cloud Gateway 入門案例SpringCloudGateway
- spring學習:spring原始碼_BeanDefinitionSpring原始碼Bean
- Spring5.0原始碼學習系列之Spring AOP簡述Spring原始碼
- Spring Cloud Netflix—如何加入HystrixSpringCloud
- Spring Cloud Hystrix的請求合併SpringCloud
- Spring Cloud Gateway入門 - spring.ioSpringCloudGateway