原創不易,如若轉載 請標明來源!java
歡迎關注本人微信公衆號:壹枝花算不算浪漫
更多內容也可查看本人博客:一枝花算不算浪漫git
上一個系列文章講解了Feign的源碼,主要是Feign動態代理實現的原理,及配合Ribbon實現負載均衡的機制。github
這裏咱們講解一個新的組件Hystrix,也是和Feign進行融合的。spring
這一講開始講解Hystrix相關代碼,固然仍是基於上一個組件Feign的基礎上開始講解的,這裏默認你對Feign已經有了大體的瞭解。編程
使用過spring cloud的小夥伴對這個組件都不會陌生,Hystrix是保證系統高可用一個很重要的組件,主要提供一下幾個功能:緩存
目錄以下:微信
將全部請求外部系統(或者叫依賴服務)的邏輯封裝到 HystrixCommand 或者 HystrixObservableCommand 對象中。網絡
Run()方法爲實現業務邏輯,這些邏輯將會在獨立的線程中被執行當請求依賴服務時出現拒絕服務、超時或者短路(多個依賴服務順序請求,前面的依賴服務請求失敗,則後面的請求不會發出)時,執行該依賴服務的失敗回退邏輯(Fallback)。併發
Hystrix 爲每一個依賴項維護一個小線程池(或信號量);若是它們達到設定值(觸發隔離),則發往該依賴項的請求將當即被拒絕,執行失敗回退邏輯(Fallback),而不是排隊。app
隔離策略分線程隔離和信號隔離。
線程隔離
第三方客戶端(執行Hystrix的run()方法)會在單獨的線程執行,會與調用的該任務的線程進行隔離,以此來防止調用者調用依賴所消耗的時間過長而阻塞調用者的線程。
使用線程隔離的好處:
線程池的缺點
線程最主要的缺點就是增長了CPU的計算開銷,每一個command都會在單獨的線程上執行,這樣的執行方式會涉及到命令的排隊、調度和上下文切換。
Netflix在設計這個系統時,決定接受這個開銷的代價,來換取它所提供的好處,而且認爲這個開銷是足夠小的,不會有重大的成本或者是性能影響。
信號隔離
信號隔離是經過限制依賴服務的併發請求數,來控制隔離開關。信號隔離方式下,業務請求線程和執行依賴服務的線程是同一個線程(例如Tomcat容器線程)。
熔斷機制是一種保護性機制,當系統中某個服務失敗率太高時,將開啓熔斷器,對該服務的後續調用,直接拒絕,進行Fallback操做。
熔斷所依靠的數據便是Metrics中的HealthCount所統計的錯誤率。
如何判斷是否應該開啓熔斷器?
必須同時知足兩個條件:
當construct()或run()執行失敗時,Hystrix調用fallback執行回退邏輯,回退邏輯包含了通用的響應信息,這些響應從內存緩存中或者其餘固定邏輯中獲得,而不該有任何的網絡依賴。
若是必定要在失敗回退邏輯中包含網絡請求,必須將這些網絡請求包裝在另外一個 HystrixCommand 或 HystrixObservableCommand 中,即屢次降級。
失敗降級也有頻率限時,若是同一fallback短期請求過大,則會拋出拒絕異常。
同一對象的不一樣HystrixCommand實例,只執行一次底層的run()方法,並將第一個響應結果緩存起來,其後的請求都會從緩存返回相同的數據。
因爲請求緩存位於construct()或run()方法調用以前,因此,它減小了線程的執行,消除了線程、上下文等開銷。
用一張簡單地流程圖總結:
Demo工程仍是使用以前的項目,git地址:https://github.com/barrywangmeng/spring-cloud-learn
eureka-server:註冊中心
serviceA: 提供對外接口
serviceB: 經過feign調用serviceA接口
在serviceB項目中添加hystrix相關pom依賴及配置,這裏就不列出來了,小夥伴們能夠直接下載這個項目看一下。
接着就是改造對serviceA調用的FeignClient:
咱們能夠調整serviceB中feign調用超時時間配置類模擬觸發Hystrix降級邏輯:
咱們在調試的過程當中,爲了方便走正常不降級邏輯的debug調試,特意會修改feign及hystrix的超時時間。
由於hystrix中大量使用了響應式編程(rxJava),代碼中包含大量的觀察者模式設計,各類回調函數糅雜在一塊兒,因此代碼顯得很難懂。
這裏咱們不糾結更多的rxJava源碼,爲了調試,每一個回調方法都會打上斷點。
關於Hystrix daboard相關的內容這裏也不會講解,實際項目中會使用其餘第三方組件來作服務監控,這裏不作更多研究。
以前咱們講過,若是不配置feign.hystrix.enabled:true這個配置的話,默認用的是DefaultTargeter
, 配置了的話就改變爲HystrixTargeter
。
咱們來看看HystrixTargeter.target()
方法:
class HystrixTargeter implements Targeter { @Override public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) { if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { return feign.target(target); } // 裏面包含encoder、decoder等feign的組件信息 feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign; // factory.getName: serviceA 返回的setterFactory 是null SetterFactory setterFactory = getOptional(factory.getName(), context, SetterFactory.class); if (setterFactory != null) { builder.setterFactory(setterFactory); } // 獲取設置的feignClient的fallback屬性 Class<?> fallback = factory.getFallback(); if (fallback != void.class) { return targetWithFallback(factory.getName(), context, target, builder, fallback); } // 獲取設置的feignClient的fallbackFactory屬性 Class<?> fallbackFactory = factory.getFallbackFactory(); if (fallbackFactory != void.class) { // 配置了降級factory的話,直接進入這個邏輯 return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory); } return feign.target(target); } private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context, Target.HardCodedTarget<T> target, HystrixFeign.Builder builder, Class<?> fallbackFactoryClass) { FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class); // 調用咱們自定義的fallback工廠中的create方法 Object exampleFallback = fallbackFactory.create(new RuntimeException()); Assert.notNull(exampleFallback, String.format( "Incompatible fallbackFactory instance for feign client %s. Factory may not produce null!", feignClientName)); // target.type() 就是ServiceAFeignClient 這個feignClient接口的名稱 這裏就是作些判斷 if (!target.type().isAssignableFrom(exampleFallback.getClass())) { throw new IllegalStateException( String.format( "Incompatible fallbackFactory instance for feign client %s. Factory produces instances of '%s', but should produce instances of '%s'", feignClientName, exampleFallback.getClass(), target.type())); } // 執行HystrixFeign中的target方法 return builder.target(target, fallbackFactory); } }
咱們設置的這個FallbackFactory負責在每次超時、拒絕(線程池滿)、異常的時候,create()方法返回一個降級機制的對象
從服務(ServiceA)的獨立的spring容器中取出來一個獨立的FallbackFactory,調用每一個服務的時候,他對應的FallbackFactory都是存在於那個服務關聯的獨立的spring容器中的。
接着進入到Hystrix.target()
中:
public final class HystrixFeign { public static Builder builder() { return new Builder(); } public static final class Builder extends Feign.Builder { private Contract contract = new Contract.Default(); private SetterFactory setterFactory = new SetterFactory.Default(); /** * @see #target(Class, String, FallbackFactory) */ public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) { return build(fallbackFactory).newInstance(target); } Feign build(final FallbackFactory<?> nullableFallbackFactory) { super.invocationHandlerFactory(new InvocationHandlerFactory() { @Override public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) { // 設置invocationHandlerFactory爲HystrixInvocationHandler return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory); } }); // 設置contact爲HystrixDelegatingContract super.contract(new HystrixDelegatingContract(contract)); // 調用父類的build方法 return super.build(); } } } public class ReflectiveFeign extends Feign { public <T> T newInstance(Target<T> target) { Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>(); List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>(); for (Method method : target.type().getMethods()) { if (method.getDeclaringClass() == Object.class) { continue; } else if(Util.isDefault(method)) { DefaultMethodHandler handler = new DefaultMethodHandler(method); defaultMethodHandlers.add(handler); methodToHandler.put(method, handler); } else { methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } } // 和以前同樣,生成一個JDK動態代理對象 InvocationHandler handler = factory.create(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler); for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy; } }
最終實際用來去處理這個請求的,實際上是InvocationHandler,他是JDK動態代理的核心,基於JDK動態代理機制,生成一個動態代理的對象以後,對這個對象全部的方法調用,都會走關聯的那個InvocationHandler。
咱們這裏設置的是HystrixInvocationHandler
,來看下它的構造參數:
接下來還設置了contract信息,Contract是解析第三方註解的組件,設置爲了HystrixDelegatingContract,顧名思義,就是說,設置了這個組件以後,後面就能夠解析你在各個接口上hystrix相關的一些註解。
上面已經分析了Hystrix基礎原理與Demo的搭建,基礎原理中用一張簡單地圖畫了Hystrix實現的流程,後面會更加詳細的依據這個圖進行講解。
本文章首發自本人博客:https://www.cnblogs.com/wang-meng 和公衆號:壹枝花算不算浪漫,如若轉載請標明來源!
感興趣的小夥伴可關注我的公衆號:壹枝花算不算浪漫