原文鏈接:http://blog.didispace.com/springcloud3/
在微服務(wù)架構(gòu)中伞芹,我們將系統(tǒng)拆分成了一個(gè)個(gè)的服務(wù)單元幌甘,各單元間通過(guò)服務(wù)注冊(cè)與訂閱的方式互相依賴(lài)。由于每個(gè)單元都在不同的進(jìn)程中運(yùn)行,依賴(lài)通過(guò)遠(yuǎn)程調(diào)用的方式執(zhí)行,這樣就有可能因?yàn)榫W(wǎng)絡(luò)原因或是依賴(lài)服務(wù)自身問(wèn)題出現(xiàn)調(diào)用故障或延遲,而這些問(wèn)題會(huì)直接導(dǎo)致調(diào)用方的對(duì)外服務(wù)也出現(xiàn)延遲峦朗,若此時(shí)調(diào)用方的請(qǐng)求不斷增加,最后就會(huì)出現(xiàn)因等待出現(xiàn)故障的依賴(lài)方響應(yīng)而形成任務(wù)積壓排龄,最終導(dǎo)致自身服務(wù)的癱瘓波势。
舉個(gè)例子,在一個(gè)電商網(wǎng)站中橄维,我們可能會(huì)將系統(tǒng)拆分成尺铣,用戶(hù)、訂單争舞、庫(kù)存凛忿、積分、評(píng)論等一系列的服務(wù)單元竞川。用戶(hù)創(chuàng)建一個(gè)訂單的時(shí)候店溢,在調(diào)用訂單服務(wù)創(chuàng)建訂單的時(shí)候,會(huì)向庫(kù)存服務(wù)來(lái)請(qǐng)求出貨(判斷是否有足夠庫(kù)存來(lái)出貨)委乌。此時(shí)若庫(kù)存服務(wù)因網(wǎng)絡(luò)原因無(wú)法被訪問(wèn)到床牧,導(dǎo)致創(chuàng)建訂單服務(wù)的線程進(jìn)入等待庫(kù)存申請(qǐng)服務(wù)的響應(yīng),在漫長(zhǎng)的等待之后用戶(hù)會(huì)因?yàn)檎?qǐng)求庫(kù)存失敗而得到創(chuàng)建訂單失敗的結(jié)果遭贸。如果在高并發(fā)情況之下戈咳,因這些等待線程在等待庫(kù)存服務(wù)的響應(yīng)而未能釋放,使得后續(xù)到來(lái)的創(chuàng)建訂單請(qǐng)求被阻塞,最終導(dǎo)致訂單服務(wù)也不可用著蛙。
在微服務(wù)架構(gòu)中删铃,存在著那么多的服務(wù)單元,若一個(gè)單元出現(xiàn)故障踏堡,就會(huì)因依賴(lài)關(guān)系形成故障蔓延猎唁,最終導(dǎo)致整個(gè)系統(tǒng)的癱瘓,這樣的架構(gòu)相較傳統(tǒng)架構(gòu)就更加的不穩(wěn)定顷蟆。為了解決這樣的問(wèn)題胖秒,因此產(chǎn)生了斷路器模式。
斷路器模式源于Martin Fowler的Circuit Breaker一文慕的。“斷路器”本身是一種開(kāi)關(guān)裝置挤渔,用于在電路上保護(hù)線路過(guò)載肮街,當(dāng)線路中有電器發(fā)生短路時(shí),“斷路器”能夠及時(shí)的切斷故障電路判导,防止發(fā)生過(guò)載嫉父、發(fā)熱、甚至起火等嚴(yán)重后果眼刃。
在分布式架構(gòu)中绕辖,斷路器模式的作用也是類(lèi)似的,當(dāng)某個(gè)服務(wù)單元發(fā)生故障(類(lèi)似用電器發(fā)生短路)之后擂红,通過(guò)斷路器的故障監(jiān)控(類(lèi)似熔斷保險(xiǎn)絲)仪际,向調(diào)用方返回一個(gè)錯(cuò)誤響應(yīng),而不是長(zhǎng)時(shí)間的等待昵骤。這樣就不會(huì)使得線程因調(diào)用故障服務(wù)被長(zhǎng)時(shí)間占用不釋放树碱,避免了故障在分布式系統(tǒng)中的蔓延。
在Spring Cloud中使用了Hystrix?來(lái)實(shí)現(xiàn)斷路器的功能变秦。Hystrix是Netflix開(kāi)源的微服務(wù)框架套件之一成榜,該框架目標(biāo)在于通過(guò)控制那些訪問(wèn)遠(yuǎn)程系統(tǒng)、服務(wù)和第三方庫(kù)的節(jié)點(diǎn)蹦玫,從而對(duì)延遲和故障提供更強(qiáng)大的容錯(cuò)能力赎婚。Hystrix具備擁有回退機(jī)制和斷路器功能的線程和信號(hào)隔離,請(qǐng)求緩存和請(qǐng)求打包樱溉,以及監(jiān)控和配置等功能挣输。
下面我們來(lái)看看如何使用Hystrix。
在開(kāi)始加入斷路器之前饺窿,我們先拿之前構(gòu)建兩個(gè)微服務(wù)為基礎(chǔ)進(jìn)行下面的操作歧焦,主要使用下面幾個(gè)工程:
eureka-server工程:服務(wù)注冊(cè)中心,端口1111
compute-service工程:服務(wù)單元,端口2222
eureka-ribbon:通過(guò)ribbon實(shí)現(xiàn)的服務(wù)單元绢馍,依賴(lài)compute-service的服務(wù)向瓷,端口3333
eureka-feign:通過(guò)feign實(shí)現(xiàn)的服務(wù)單元,依賴(lài)compute-service的服務(wù)舰涌,端口3333
若您還沒(méi)有使用Spring Cloud的經(jīng)驗(yàn)猖任,可以先閱讀《服務(wù)注冊(cè)與發(fā)現(xiàn)》與《服務(wù)消費(fèi)者》,對(duì)Spring Cloud構(gòu)建的微服務(wù)有一個(gè)初步的認(rèn)識(shí)瓷耙。
依次啟動(dòng)eureka-server朱躺、compute-service、eureka-ribbon工程
訪問(wèn)http://localhost:1111/可以看到注冊(cè)中心的狀態(tài)
訪問(wèn)http://localhost:3333/add搁痛,調(diào)用eureka-ribbon的服務(wù)长搀,該服務(wù)會(huì)去調(diào)用compute-service的服務(wù),計(jì)算出10+20的值鸡典,頁(yè)面顯示30
關(guān)閉compute-service服務(wù)源请,訪問(wèn)http://localhost:3333/add,我們獲得了下面的報(bào)錯(cuò)信息
Whitelabel Error Page
This application has no explicit mappingfor/error, so you are seeing this as a fallback.
Sat Jun 25 21:16:59 CST 2016
There was an unexpected error (type=Internal Server Error, status=500).
I/O error on GET requestfor"http://COMPUTE-SERVICE/add?a=10&b=20": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect
pom.xml中引入依賴(lài)hystrix依賴(lài)
org.springframework.cloud
spring-cloud-starter-hystrix
在eureka-ribbon的主類(lèi)RibbonApplication中使用@EnableCircuitBreaker注解開(kāi)啟斷路器功能:
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
publicclassRibbonApplication{
@Bean
@LoadBalanced
RestTemplaterestTemplate(){
returnnewRestTemplate();
}
publicstaticvoidmain(String[] args){
SpringApplication.run(RibbonApplication.class, args);
}
}
改造原來(lái)的服務(wù)消費(fèi)方式彻况,新增ComputeService類(lèi)谁尸,在使用ribbon消費(fèi)服務(wù)的函數(shù)上增加@HystrixCommand注解來(lái)指定回調(diào)方法。
@Service
publicclassComputeService{
@Autowired
? ? RestTemplate restTemplate;
@HystrixCommand(fallbackMethod ="addServiceFallback")
publicStringaddService(){
returnrestTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
? ? }
publicStringaddServiceFallback(){
return"error";
? ? }
}
提供rest接口的Controller改為調(diào)用ComputeService的addService
@RestController
publicclassConsumerController{
@Autowired
privateComputeService computeService;
@RequestMapping(value ="/add", method = RequestMethod.GET)
publicStringadd(){
returncomputeService.addService();
? ? }
}
驗(yàn)證斷路器的回調(diào)
依次啟動(dòng)eureka-server纽甘、compute-service良蛮、eureka-ribbon工程
訪問(wèn)http://localhost:1111/可以看到注冊(cè)中心的狀態(tài)
訪問(wèn)http://localhost:3333/add,頁(yè)面顯示:30
關(guān)閉compute-service服務(wù)后再訪問(wèn)http://localhost:3333/add悍赢,頁(yè)面顯示:error
注意這里說(shuō)的是“使用”决瞳,沒(méi)有錯(cuò),我們不需要在Feigh工程中引入Hystix左权,F(xiàn)eign中已經(jīng)依賴(lài)了Hystrix瞒斩,我們可以在未做任何改造前,嘗試下面你的操作:
依次啟動(dòng)eureka-server涮总、compute-service胸囱、eureka-feign工程
訪問(wèn)http://localhost:1111/可以看到注冊(cè)中心的狀態(tài)
訪問(wèn)http://localhost:3333/add,調(diào)用eureka-feign的服務(wù)瀑梗,該服務(wù)會(huì)去調(diào)用compute-service的服務(wù)烹笔,計(jì)算出10+20的值,頁(yè)面顯示30
關(guān)閉compute-service服務(wù)抛丽,訪問(wèn)http://localhost:3333/add谤职,我們獲得了下面的報(bào)錯(cuò)信息
Whitelabel Error Page
This application has no explicit mappingfor/error, so you are seeing this as a fallback.
Sat Jun 25 22:10:05 CST 2016
There was an unexpected error (type=Internal Server Error, status=500).
add timed-out and no fallback available.
如果您夠仔細(xì),會(huì)發(fā)現(xiàn)與在ribbon中的報(bào)錯(cuò)是不同的亿鲜,看到add timed-out and no fallback available這句允蜈,或許您已經(jīng)猜到什么冤吨,看看我們的控制臺(tái),可以看到報(bào)錯(cuò)信息來(lái)自hystrix-core-1.5.2.jar饶套,所以在這個(gè)工程中漩蟆,我們要學(xué)習(xí)的就是如何使用Feign中集成的Hystrix。
使用@FeignClient注解中的fallback屬性指定回調(diào)類(lèi)
@FeignClient(value ="compute-service", fallback = ComputeClientHystrix.class)
publicinterfaceComputeClient{
@RequestMapping(method = RequestMethod.GET, value ="/add")
Integeradd(@RequestParam(value ="a")Integer a, @RequestParam(value ="b")Integer b);
}
創(chuàng)建回調(diào)類(lèi)ComputeClientHystrix妓蛮,實(shí)現(xiàn)@FeignClient的接口怠李,此時(shí)實(shí)現(xiàn)的方法就是對(duì)應(yīng)@FeignClient接口中映射的fallback函數(shù)。
@Component
publicclassComputeClientHystriximplementsComputeClient{
@Override
publicIntegeradd(@RequestParam(value ="a")Integer a, @RequestParam(value ="b")Integer b){
return-9999;
? ? }
}
再用之前的方法驗(yàn)證一下蛤克,是否在compute-service服務(wù)不可用的情況下捺癞,頁(yè)面返回了-9999。