技術(shù)需求點(diǎn):
在SpringCloud+SpringBoot部署的微服務(wù)系統(tǒng)中,模擬服務(wù)故障,證明Hystrix組件可以保證當(dāng)服務(wù)提供者提供的服務(wù)不可用時(shí),調(diào)用方可以切換到降級(jí)后的執(zhí)行策略晌端,保持系統(tǒng)服務(wù)穩(wěn)定性,同時(shí)介紹Hystrix的算法機(jī)制和原理恬砂。
Hystrix是一種叫豪豬的動(dòng)物咧纠,身體后半部分長(zhǎng)滿了圓棘刺,當(dāng)遇到危險(xiǎn)時(shí)泻骤,通過后退的攻擊方式將棘刺插入敵人身體漆羔,以保護(hù)自己不受傷害。
前言:分布式系統(tǒng)服務(wù)雪崩問題
在分布式系統(tǒng)中狱掂,多個(gè)服務(wù)之間通常是通過Rpc遠(yuǎn)程調(diào)用的方式實(shí)現(xiàn)的演痒,這種服務(wù)依賴的模式可能會(huì)帶來問題,如果被依賴的服務(wù)A掛掉趋惨,而調(diào)用者B沒采取任何防御措施鸟顺,可能會(huì)導(dǎo)致調(diào)用者B的所有線程在數(shù)秒內(nèi)處于阻塞狀態(tài)釋放不了,后續(xù)請(qǐng)求累積增加器虾,可能導(dǎo)致調(diào)用者B癱瘓讯嫂,服務(wù)B癱瘓又會(huì)導(dǎo)致B的調(diào)用者C癱瘓......最后引起服務(wù)雪崩問題。因此兆沙,一定的防御措施(降級(jí)策略)需要在系統(tǒng)設(shè)計(jì)時(shí)考慮到欧芽,當(dāng)服務(wù)調(diào)用方發(fā)現(xiàn)提供方的服務(wù)出現(xiàn)異常時(shí),調(diào)用方能夠快速切換到預(yù)置好的降級(jí)策略中葛圃。
正常時(shí)千扔,調(diào)用者(UserRequest)調(diào)用Dependency A、H库正、I曲楚、P服務(wù):
當(dāng)Dependcency I系統(tǒng)出現(xiàn)問題,調(diào)用者UserRequest也會(huì)阻塞:
在請(qǐng)求量較大時(shí)诀诊,在數(shù)秒內(nèi)所有線程會(huì)阻塞洞渤,導(dǎo)致所有資源飽和,系統(tǒng)無法再提供服務(wù)属瓣。
一.Hystrix基于自反饋調(diào)節(jié)熔斷狀態(tài)的算法原理
在電路系統(tǒng)中载迄,會(huì)有保險(xiǎn)絲,當(dāng)電流過大產(chǎn)熱過大時(shí)抡蛙,保險(xiǎn)絲會(huì)熔斷以切斷與外部電路的聯(lián)通护昧。Hystrix充當(dāng)了保險(xiǎn)絲的角色,當(dāng)請(qǐng)求量過大且請(qǐng)求錯(cuò)誤量超過我們?cè)O(shè)定的閾值時(shí)粗截,Hystrix的熔斷器會(huì)熔斷惋耙,讓調(diào)用者服務(wù)自動(dòng)降級(jí)以保護(hù)服務(wù)。自動(dòng)降級(jí)后的服務(wù)不會(huì)再遠(yuǎn)程調(diào)用依賴方的服務(wù),轉(zhuǎn)向我們的降級(jí)策略去執(zhí)行绽榛,比如給個(gè)失敗頁面快速失敗湿酸,減少調(diào)用方的漫長(zhǎng)等待。
此外灭美,熔斷開啟推溃,服務(wù)降級(jí)后,Hystrix有自動(dòng)恢復(fù)的功能届腐,假如我們?cè)O(shè)定每次熔斷后休眠5秒鐘(5秒鐘內(nèi)不調(diào)用遠(yuǎn)程服務(wù))铁坎,到時(shí)間后會(huì)自動(dòng)重試,讓20次請(qǐng)求嘗試調(diào)用遠(yuǎn)程服務(wù)犁苏,如果能返回正常結(jié)果硬萍,就認(rèn)為服務(wù)恢復(fù)正常,熔斷器自動(dòng)關(guān)閉围详,服務(wù)降級(jí)停止朴乖。
根據(jù)這一過程,Hystrix的狀態(tài)機(jī)分為三種狀態(tài):open助赞、closed寒砖、half-open,熔斷器會(huì)在這三種狀態(tài)之間切換嫉拐。具體來講:
- open:被調(diào)用方在一定時(shí)間窗內(nèi)請(qǐng)求錯(cuò)誤率大于設(shè)定的閾值,被判定為異常魁兼,熔斷開啟婉徘,服務(wù)降級(jí),調(diào)用方執(zhí)行本地的降級(jí)策略咐汞,不進(jìn)行遠(yuǎn)程調(diào)用盖呼;
- closed:被調(diào)用服務(wù)正常,熔斷關(guān)閉化撕,調(diào)用方遠(yuǎn)程調(diào)用服務(wù)几晤。
- half-open:嘗試的中間狀態(tài),調(diào)用方熔斷且在休眠了一段時(shí)間后植阴,調(diào)用方發(fā)起測(cè)試性的遠(yuǎn)程調(diào)用蟹瘾,測(cè)試被調(diào)用者是否正常。
熔斷器的開關(guān)狀態(tài)取決于HystrixCommandMetrics對(duì)象里面的數(shù)據(jù)掠手,該對(duì)象存儲(chǔ)了類似遠(yuǎn)程接口調(diào)用次數(shù)憾朴,失敗次數(shù)等數(shù)據(jù),以時(shí)間窗口的形式統(tǒng)計(jì)各維度數(shù)據(jù)喷鸽。
二.模擬Hystrix實(shí)戰(zhàn)
1.pom依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.開啟Hystrix
在EurekaApplication文件上添加注解@EnableCircuitBreaker開啟熔斷
@EnableCircuitBreaker//開啟熔斷
@EnableEurekaClient//開啟Eureka客戶端
@SpringBootApplication
@EnableFeignClients//開啟Feign客戶端
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
添加之后众雷,感覺注解越來越多,因此可以合并下。
首先將@EnableEurekaClient替換成@EnableDiscoveryClient砾省,兩者的共同點(diǎn)是:都能讓注冊(cè)中心發(fā)現(xiàn)自己的服務(wù)鸡岗,區(qū)別是@EnableEurekaClient只支持Eureka作為注冊(cè)中心,而@EnableDiscoveryClient支持其它注冊(cè)中心编兄,比如consul轩性。
然后將@EnableCircuitBreaker、@EnableEurekaClient翻诉、@SpringBootApplication替換成@SpringCloudApplication炮姨,看@SpringCloudApplication的代碼可以看出,該注解就是集成了前面三個(gè)碰煌。
3.調(diào)用者接口
對(duì)getUsers()方法進(jìn)行熔斷舒岸,需要給這個(gè)方法寫一個(gè)對(duì)應(yīng)的fallback方法,當(dāng)調(diào)用提供者服務(wù)出現(xiàn)異常時(shí)芦圾,Hystrix會(huì)自動(dòng)執(zhí)行fallback的方法蛾派。當(dāng)然,需要給方法指定熔斷方法个少,用@HystrixCommand注解聲明洪乍,
(如果想給類的所有方法都指定一個(gè)熔斷方法夜焦,使用@DefaultProperties(defaultFallback="getUsersFallback")壳澳,需要熔斷的方法再加@HystrixCommand才行)。注解里面還可以進(jìn)行各種Hystrix的配置茫经,例如熔斷時(shí)間等等巷波,下面再說。
@RestController
public class UserController {
@Autowired
private UserFeignService userFeignService;
@RequestMapping("/consumer")
@HystrixCommand(fallbackMethod = "getUsersFallback")
public String getUsers(){
return userFeignService.getUsers();
}
public String getUsersFallback(){
return "調(diào)用的服務(wù)出現(xiàn)異常了";
}
}
其中卸伞,UserFeignService的代碼如下:
//開啟feign客戶端抹镊,eureka-provider-user微服務(wù)的serviceid(唯一)
@FeignClient("eureka-provider")
public interface UserFeignService {
@RequestMapping("/user")
public String getUsers();
}
4.被調(diào)用者接口
@RestController
public class UserController {
@RequestMapping("/user")
public String getUsers(){
return "provider的User服務(wù)";
}
}
完整的基于Eureka注冊(cè)中心的微服務(wù)搭建過程,請(qǐng)參考SpringCloud+SpringBoot搭建服務(wù)注冊(cè)與調(diào)用平臺(tái)這篇文章荤傲。
5.測(cè)試結(jié)果
當(dāng)consumer能正常調(diào)用provider服務(wù)時(shí)垮耳,
此時(shí),停掉provider服務(wù)遂黍,再次調(diào)用:
說明熔斷成功终佛,重啟provider服務(wù),又可以正常調(diào)用了雾家。
6.將熔斷放在service層級(jí)
上面的熔斷方法我們是放在controller層級(jí)的查蓉,但這給業(yè)務(wù)帶來了極大的耦合,我們希望把service層就提供了熔斷服務(wù)榜贴,controller層只調(diào)用即可豌研,出現(xiàn)異常在service層處理掉妹田。我們使用feign組件發(fā)起遠(yuǎn)程調(diào)用, 因此鹃共,在使用feign組件基礎(chǔ)上鬼佣,我們這樣優(yōu)化:
(1)先創(chuàng)建一個(gè)實(shí)現(xiàn)類UserFeignFallback implements UserFeignService,
@Component
public class UserFeignFallback implements UserFeignService {
@Override
public String getUsers() {
return "Service層:調(diào)用的服務(wù)出現(xiàn)異常了";
}
}
(2)在UserFeignService指定fallback的方法:
@FeignClient(value="eureka-provider",fallback = UserFeignFallback.class)
public interface UserFeignService {
@RequestMapping("/user")
public String getUsers();
}
(3)在配置文件中開啟feign的hystrix熔斷功能:
feign.hystrix.enabled=true
(4)最后把controller的接口改成普通的調(diào)用接口即可:
@RequestMapping("/consumer")
public String getUsers(){
return userFeignService.getUsers();
}
三.什么情況下會(huì)觸發(fā)fallback方法霜浴?
(引自參考4)
事件 | 描述 | 觸發(fā)fallback |
---|---|---|
EMIT | 值傳遞 | NO |
SUCCESS | 執(zhí)行完成晶衷,沒有錯(cuò)誤 | NO |
FAILURE | 執(zhí)行拋出異常 | YES |
TIMEOUT | 執(zhí)行開始,但沒有在允許的時(shí)間內(nèi)完成 | YES |
BAD_REQUEST | 執(zhí)行拋出HystrixBadRequestException | NO |
SHORT_CIRCUITED | 斷路器打開阴孟,不嘗試執(zhí)行 | YES |
THREAD_POOL_REJECTED | 線程池拒絕晌纫,不嘗試執(zhí)行 | YES |
SEMAPHORE_REJECTED | 信號(hào)量拒絕,不嘗試執(zhí)行 | YES |
四.Hystrix可以配置哪些參數(shù)
配置方式有2種永丝,一個(gè)是在注解@HystrixCommand屬性里面配置(比較麻煩)锹漱,一個(gè)是在配置文件中配置,兩種都可以慕嚷,但在注解中配置的優(yōu)先級(jí)更高哥牍,下面是一些常用的配置
超時(shí)時(shí)間(默認(rèn)1000ms,單位:ms)
1.hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
調(diào)用者的配置文件中配置喝检,調(diào)用者所有方法的超時(shí)時(shí)間都是該值
2.hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds
調(diào)用者的指定方法中配置嗅辣,優(yōu)先級(jí)高于上邊的全局配置統(tǒng)計(jì)次數(shù)
circuitBreaker.requestVolumeThreshold
當(dāng)在配置時(shí)間窗口內(nèi)達(dá)到此數(shù)量的失敗后,進(jìn)行熔斷挠说,默認(rèn)20個(gè)close休眠試間
circuitBreaker.sleepWindowInMilliseconds
熔斷多久后開始嘗試是否恢復(fù)澡谭,默認(rèn)5s出錯(cuò)百分比,
circuitBreaker.errorThresholdPercentage
當(dāng)錯(cuò)誤超過該百分比觸發(fā)熔斷损俭,默認(rèn)50%線程池核心線程數(shù)
hystrix.threadpool.default.coreSize(默認(rèn)為10)
后記:Hystrix現(xiàn)狀-官方社區(qū)已死
Hystrix官網(wǎng)已經(jīng)不再維護(hù)新版本译暂,但是因?yàn)槠浒姹据^為穩(wěn)定,現(xiàn)階段不影響用戶對(duì)它的正常使用撩炊。Hystrix的替代方案有兩個(gè):
1.Alibaba Sentinel
Sentinel在阿里內(nèi)部已經(jīng)被大規(guī)模采用,非常穩(wěn)定崎脉,目前在Spring Cloud的孵化器項(xiàng)目Spring Cloud Alibaba中拧咳,是阿里開源的一款斷路器實(shí)現(xiàn)。
2.Resilience4J
Resilience4J是Hystrix官方推薦的替代方案囚灼,是一款輕量級(jí)框架骆膝,使用簡(jiǎn)單,并且文檔非常清晰灶体、豐富阅签。
Tips:現(xiàn)在我們?cè)賮砜春镭i這種動(dòng)物,是不是跟hystrix的機(jī)制原理一模一樣蝎抽,當(dāng)發(fā)現(xiàn)各種服務(wù)故障(敵人)時(shí)政钟,調(diào)用fallback方法(倒退)快速失敗,提高系統(tǒng)抵御災(zāi)害能力,作者不但寫出了這么強(qiáng)大的組件养交,還命名了這么貼切的名字精算,膜拜!
參考文章:
參考1碎连,原理簡(jiǎn)介:http://www.reibang.com/p/b8d3eb0cf721?utm_source=oschina-app
參考2灰羽,源碼解析:http://www.reibang.com/p/684b04b6c454
參考3,Hystrix介紹:https://www.cnblogs.com/cjsblog/p/9391819.html
參考4鱼辙,詳細(xì)屬性配置:https://blog.csdn.net/tongtong_use/article/details/78611225