Spring Boot中如何使用斷路器詳解
前言
隨著使用 Spring 進(jìn)行開(kāi)發(fā)的個(gè)人和企業(yè)越來(lái)越多,Spring 也慢慢從一個(gè)單一簡(jiǎn)潔的小框架變成一個(gè)大而全的開(kāi)源軟件,Spring 的邊界不斷的進(jìn)行擴(kuò)充,到了后來(lái) Spring 幾乎可以做任何事情了,市面上主流的開(kāi)源軟件、中間件都有 Spring 對(duì)應(yīng)組件支持,人們?cè)谙碛?Spring 的這種便利之后,也遇到了一些問(wèn)題。
斷路器本身是電路上的一種過(guò)載保護(hù)裝置,當(dāng)線(xiàn)路中有電器發(fā)生短路時(shí),它能夠及時(shí)的切斷故障電路以防止嚴(yán)重后果發(fā)生。通過(guò)服務(wù)熔斷(也可以稱(chēng)為斷路)、降級(jí)、限流(隔離)、異步RPC等手段控制依賴(lài)服務(wù)的延遲與失敗,防止整個(gè)服務(wù)雪崩。一個(gè)斷路器可以裝飾并且檢測(cè)了一個(gè)受保護(hù)的功能調(diào)用。根據(jù)當(dāng)前的狀態(tài)決定調(diào)用時(shí)被執(zhí)行還是回退。通常情況下,一個(gè)斷路器實(shí)現(xiàn)三種類(lèi)型的狀態(tài):open、half-open以及closed:
- closed狀態(tài)的調(diào)用被執(zhí)行,事務(wù)度量被存儲(chǔ),這些度量是實(shí)現(xiàn)一個(gè)健康策略所必備的。
- 倘若系統(tǒng)健康狀況變差,斷路器就處在open狀態(tài)。此種狀態(tài)下,所有調(diào)用會(huì)被立即回退并且不會(huì)產(chǎn)生新的調(diào)用。open狀態(tài)的目的是給服務(wù)器端回復(fù)和處理問(wèn)題的時(shí)間。
- 一旦斷路器進(jìn)入一個(gè)open狀態(tài),超時(shí)計(jì)時(shí)器開(kāi)始計(jì)時(shí)。如果計(jì)時(shí)器超時(shí),斷路器切換到half-open狀態(tài)。在half-open狀態(tài)調(diào)用間歇性執(zhí)行以確定問(wèn)題是否已解決。如果解決,狀態(tài)切換回closed狀態(tài)。
斷路器背后的基本思想非常簡(jiǎn)單。將受保護(hù)的函數(shù)調(diào)用包裝在斷路器對(duì)象中,該對(duì)象監(jiān)視故障。一旦故障達(dá)到某個(gè)閾值,斷路器就會(huì)跳閘,并且所有對(duì)斷路器的進(jìn)一步調(diào)用都會(huì)返回錯(cuò)誤,而根本不會(huì)進(jìn)行受保護(hù)的呼叫。通常,如果斷路器跳閘,您還需要某種監(jiān)控器警報(bào)。
如何快速使用Hystrix呢?下面跟著我1234……
1、加入@EnableCircuitBreaker注解
@EnableCircuitBreaker @SpringBootApplication @EnableEurekaClient @EnableFeignClientspublic class DroolsAppApplication { public static void main(String[] args) { SpringApplication.run(DroolsAppApplication.class, args); } }
Hystrix整體執(zhí)行過(guò)程,首先,Command會(huì)調(diào)用run方法,如果run方法超時(shí)或者拋出異常,且啟用了降級(jí)處理,則調(diào)用getFallback方法進(jìn)行降級(jí);
2、使用@HystrixCommand注解
@HystrixCommand(fallbackMethod = "reliable") public String readingList() { for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } return "jinpingmei"; } public String reliable() { return "you love interesting book"; }
3、添加引用
compile("org.springframework.cloud:spring-cloud-starter-hystrix") compile('org.springframework.cloud:spring-cloud-starter-turbine')
4、設(shè)置超時(shí)時(shí)間
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
執(zhí)行結(jié)果如下:
正常應(yīng)該返回:
你不要喜歡jinpingmei,要喜歡有意思的書(shū);這樣使用好舒服啊,@EnableCircuitBreaker這個(gè)注解就這么強(qiáng)大嗎?
HystrixCommandAspect 通過(guò)AOP攔截所有的@HystrixCommand注解的方法,從而使得@HystrixCommand能夠集成到Spring boot中,
HystrixCommandAspect的關(guān)鍵代碼如下:
1.方法 hystrixCommandAnnotationPointcut() 定義攔截注解HystrixCommand
2.方法 hystrixCollapserAnnotationPointcut()定義攔截注解HystrixCollapser
3.方法methodsAnnotatedWithHystrixCommand(…)通過(guò)@Around(…)攔截所有HystrixCommand和HystrixCollapser注解的方法。詳細(xì)見(jiàn)方法注解
@Aspect public class HystrixCommandAspect { private static final Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP; static { META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder() .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory()) .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory()) .build(); } @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)") public void hystrixCommandAnnotationPointcut() { } @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)") public void hystrixCollapserAnnotationPointcut() { } @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()") public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable { Method method = getMethodFromTarget(joinPoint); Validate.notNull(method, "failed to get method from joinPoint: %s", 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"); } MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method)); MetaHolder metaHolder = metaHolderFactory.create(joinPoint); HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder); ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ? metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType(); Object result; try { result = CommandExecutor.execute(invokable, executionType, metaHolder); } catch (HystrixBadRequestException e) { throw e.getCause(); } return result; }
那么HystrixCommandAspect是如何初始化,是通過(guò)HystrixCircuitBreakerConfiguration實(shí)現(xiàn)的
@Configuration public class HystrixCircuitBreakerConfiguration { @Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); }
那么誰(shuí)來(lái)觸發(fā)HystrixCircuitBreakerConfiguration執(zhí)行初始化
先看spring-cloud-netflix-core**.jar包的spring.factories里有這段配置,是由注解EnableCircuitBreaker觸發(fā)
org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\ org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
那么@EnableCircuitBreaker如何觸發(fā)HystrixCircuitBreakerConfiguration
通過(guò)源碼查看,此類(lèi)通過(guò)@Import初始化EnableCircuitBreakerImportSelector類(lèi)
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(EnableCircuitBreakerImportSelector.class) public @interface EnableCircuitBreaker { }
EnableCircuitBreakerImportSelector是SpringFactoryImportSelector子類(lèi)。此類(lèi)在初始化后,會(huì)執(zhí)行selectImports(AnnotationMetadata metadata)的方法。此方法會(huì)根據(jù)注解啟動(dòng)的注解(這里指@EnableCircuitBreaker)從spring.factories文件中獲取其配置需要初始化@Configuration類(lèi)(這里是org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration),從而最終初始化HystrixCommandAspect 類(lèi),從而實(shí)現(xiàn)攔截HystrixCommand的功能
以上就是通過(guò)@EnableCircuitBreake可以開(kāi)啟Hystrix的原理。Hystrix用到了觀察者模式AbstractCommand.executeCommandAndObserve()模式,下次我們來(lái)深入說(shuō)一下觀察者模式。歡迎拍磚!
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
java private關(guān)鍵字用法實(shí)例
這篇文章主要介紹了java private關(guān)鍵字用法實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10java正則表達(dá)式表單驗(yàn)證類(lèi)工具類(lèi)(驗(yàn)證郵箱、手機(jī)號(hào)碼、qq號(hào)碼等)
這篇文章主要介紹了java使用正則表達(dá)式進(jìn)行表單驗(yàn)證工具類(lèi),可以驗(yàn)證郵箱、手機(jī)號(hào)碼、qq號(hào)碼等方法,需要的朋友可以參考下2014-04-04SpringBoot整合atomikos實(shí)現(xiàn)跨庫(kù)事務(wù)的詳細(xì)方案
這篇文章主要介紹了SpringBoot整合atomikos實(shí)現(xiàn)跨庫(kù)事務(wù),業(yè)務(wù)主要涉及政府及企業(yè)且并發(fā)量不大,所以采用XA事務(wù),雖然性能有所損失,但是可以保證數(shù)據(jù)的強(qiáng)一致性,需要的朋友可以參考下2022-06-06java線(xiàn)程池不同場(chǎng)景下使用示例經(jīng)驗(yàn)總結(jié)
這篇文章主要為大家介紹了java線(xiàn)程池不同場(chǎng)景如何使用的示例源碼及經(jīng)驗(yàn)總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03深入探討Druid動(dòng)態(tài)數(shù)據(jù)源的實(shí)現(xiàn)方式
Druid是一個(gè)高性能的實(shí)時(shí)分析數(shù)據(jù)庫(kù),它可以處理大規(guī)模數(shù)據(jù)集的快速查詢(xún)和聚合操作,在Druid中,動(dòng)態(tài)數(shù)據(jù)源是一種可以在運(yùn)行時(shí)動(dòng)態(tài)添加和刪除的數(shù)據(jù)源,使用動(dòng)態(tài)數(shù)據(jù)源,您可以在Druid中輕松地處理不斷變化的數(shù)據(jù)集,本文講給大家介紹一下Druid動(dòng)態(tài)數(shù)據(jù)源該如何實(shí)現(xiàn)2023-08-08java 中DH的方式實(shí)現(xiàn)非對(duì)稱(chēng)加密的實(shí)例
這篇文章主要介紹了java 中DH的方式實(shí)現(xiàn)非對(duì)稱(chēng)加密的實(shí)例的相關(guān)資料,這里提供實(shí)現(xiàn)簡(jiǎn)單實(shí)例,需要的朋友可以參考下2017-08-08