歡迎來(lái)到菜鳥(niǎo)SpringCloud實(shí)戰(zhàn)入門系列(SpringCloudForNoob)糠聪,該系列通過(guò)層層遞進(jìn)的實(shí)戰(zhàn)視角,來(lái)一步步學(xué)習(xí)和理解SpringCloud燕耿。
本系列適合有一定Java以及SpringBoot基礎(chǔ)的同學(xué)閱讀浪漠。
每篇文章末尾都附有本文對(duì)應(yīng)的Github源代碼鸵膏,方便同學(xué)調(diào)試。
實(shí)戰(zhàn)版本
- SpringBoot:2.0.3.RELEASE
- SpringCloud:Finchley.RELEASE
熔斷器Hystrix的使用
熔斷器的概念
關(guān)于熔斷器的解釋法焰,這里引用一大段:
熔斷器適用于實(shí)現(xiàn)快速失敗秧荆,如果它在一段時(shí)間內(nèi)偵測(cè)到許多類似的錯(cuò)誤,會(huì)強(qiáng)迫其以后的多個(gè)調(diào)用快速失敗埃仪,不再訪問(wèn)遠(yuǎn)程服務(wù)器乙濒,從而防止應(yīng)用程序不斷地嘗試執(zhí)行可能會(huì)失敗的操作,使得應(yīng)用程序繼續(xù)執(zhí)行而不用等待修正錯(cuò)誤,或者浪費(fèi)CPU時(shí)間去等到長(zhǎng)時(shí)間的超時(shí)產(chǎn)生颁股。熔斷器也可以使應(yīng)用程序能夠診斷錯(cuò)誤是否已經(jīng)修正么库,如果已經(jīng)修正,應(yīng)用程序會(huì)再次嘗試調(diào)用操作甘有。
熔斷器模式就像是那些容易導(dǎo)致錯(cuò)誤的操作的一種代理诉儒。這種代理能夠記錄最近調(diào)用發(fā)生錯(cuò)誤的次數(shù),然后決定使用允許操作繼續(xù)亏掀,或者立即返回錯(cuò)誤忱反。 熔斷器開(kāi)關(guān)相互轉(zhuǎn)換的邏輯如下圖:
熔斷器就是保護(hù)服務(wù)高可用的最后一道防線。
Hystrix特性
1.斷路器機(jī)制
斷路器很好理解, 當(dāng)Hystrix Command請(qǐng)求后端服務(wù)失敗數(shù)量超過(guò)一定比例(默認(rèn)50%), 斷路器會(huì)切換到開(kāi)路狀態(tài)(Open). 這時(shí)所有請(qǐng)求會(huì)直接失敗而不會(huì)發(fā)送到后端服務(wù). 斷路器保持在開(kāi)路狀態(tài)一段時(shí)間后(默認(rèn)5秒), 自動(dòng)切換到半開(kāi)路狀態(tài)(HALF-OPEN). 這時(shí)會(huì)判斷下一次請(qǐng)求的返回情況, 如果請(qǐng)求成功, 斷路器切回閉路狀態(tài)(CLOSED), 否則重新切換到開(kāi)路狀態(tài)(OPEN). Hystrix的斷路器就像我們家庭電路中的保險(xiǎn)絲, 一旦后端服務(wù)不可用, 斷路器會(huì)直接切斷請(qǐng)求鏈, 避免發(fā)送大量無(wú)效請(qǐng)求影響系統(tǒng)吞吐量, 并且斷路器有自我檢測(cè)并恢復(fù)的能力.
2.Fallback
Fallback相當(dāng)于是降級(jí)操作. 對(duì)于查詢操作, 我們可以實(shí)現(xiàn)一個(gè)fallback方法, 當(dāng)請(qǐng)求后端服務(wù)出現(xiàn)異常的時(shí)候, 可以使用fallback方法返回的值. fallback方法的返回值一般是設(shè)置的默認(rèn)值或者來(lái)自緩存.
3.資源隔離
在Hystrix中, 主要通過(guò)線程池來(lái)實(shí)現(xiàn)資源隔離. 通常在使用的時(shí)候我們會(huì)根據(jù)調(diào)用的遠(yuǎn)程服務(wù)劃分出多個(gè)線程池. 例如調(diào)用產(chǎn)品服務(wù)的Command放入A線程池, 調(diào)用賬戶服務(wù)的Command放入B線程池. 這樣做的主要優(yōu)點(diǎn)是運(yùn)行環(huán)境被隔離開(kāi)了. 這樣就算調(diào)用服務(wù)的代碼存在bug或者由于其他原因?qū)е伦约核诰€程池被耗盡時(shí), 不會(huì)對(duì)系統(tǒng)的其他服務(wù)造成影響. 但是帶來(lái)的代價(jià)就是維護(hù)多個(gè)線程池會(huì)對(duì)系統(tǒng)帶來(lái)額外的性能開(kāi)銷. 如果是對(duì)性能有嚴(yán)格要求而且確信自己調(diào)用服務(wù)的客戶端代碼不會(huì)出問(wèn)題的話, 可以使用Hystrix的信號(hào)模式(Semaphores)來(lái)隔離資源.
使用Feign Hystrix
依然使用上一章建立的子模塊:Service-Feign
pom文件不需要變化滤愕,因?yàn)閟pring-cloud-starter-openfeign已經(jīng)自帶了Hystrix温算。
修改配置文件application.yml,增加如下:
feign:
hystrix:
enabled: true
在我這里间影,由于Springboot使用了2.0.3注竿,引入的是openfeign,所以出現(xiàn)了:
起初以為這條指令無(wú)法生效魂贬,折騰了之后才發(fā)現(xiàn)是生效的巩割。
推測(cè)是openfeign的問(wèn)題,不是spring boot 1.X的spring-cloud-starter-feign随橘,IDEA判斷失誤了喂分。
接著新建類ServiceHiHystrix繼承ServiceHi接口,里面實(shí)現(xiàn)了失敗下的返回信息:
import org.springframework.stereotype.Component;
@Component
public class ServiceHiHystrix implements ServiceHi {
@Override
public String sayHiFromServiceHi(String name) {
return "hello" + name +", this message send failed";
}
}
更改ServiceHi接口机蔗,添加上fallback類為剛才新建的類:
@FeignClient(value = "service-hi", fallback = ServiceHiHystrix.class)
大功告成,啟動(dòng)server-feign甘萧,eureka-hi和eureka測(cè)試:
打開(kāi):http://localhost:8765/hello/rude3knife
接著萝嘁,關(guān)掉服務(wù)提供者eureka-hi,來(lái)模擬服務(wù)宕機(jī)的情況扬卷,重新訪問(wèn):http://localhost:8765/hello/rude3knife
返回了新建的錯(cuò)誤信息牙言。
可視化熔斷器監(jiān)控Hystrix Dashboard和Turbine
Hystrix Dashboard
Hystrix-dashboard是一款針對(duì)Hystrix進(jìn)行實(shí)時(shí)監(jiān)控的工具,通過(guò)Hystrix Dashboard我們可以在直觀地看到各Hystrix Command的請(qǐng)求響應(yīng)時(shí)間, 請(qǐng)求成功率等數(shù)據(jù)怪得。
我們只需要在上一章中的server-feign模塊繼續(xù)修改咱枉,因?yàn)樯弦徽乱呀?jīng)在該模塊加了熔斷器。
首先添加依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<!--spring boot 1.X:spring-cloud-starter-hystrix-->
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<!--spring boot 1.X:spring-cloud-starter-hystrix-dashboard-->
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
啟動(dòng)類修改:
@EnableHystrixDashboard
@EnableCircuitBreaker
啟動(dòng)工程訪問(wèn):
http://localhost:8765/hystrix徒恋,將會(huì)看到如下界面:
圖中會(huì)有一些提示:
- Cluster via Turbine (default cluster): http://turbine-hostname:port/turbine.stream
- Cluster via Turbine (custom cluster): http://turbine-hostname:port/turbine.stream?cluster=[clusterName]
- Single Hystrix App: http://hystrix-app:port/hystrix.stream
大概意思就是如果查看默認(rèn)集群使用第一個(gè)url,查看指定集群使用第二個(gè)url,單個(gè)應(yīng)用的監(jiān)控使用最后一個(gè)蚕断,我們暫時(shí)只演示單個(gè)應(yīng)用的所以在輸入框中輸入: http://localhost:8765/hystrix.stream ,輸入之后點(diǎn)擊 monitor入挣,進(jìn)入頁(yè)面亿乳。
如果沒(méi)有請(qǐng)求會(huì)先顯示Loading ...,訪問(wèn)http://localhost:9001/hystrix.stream 也會(huì)不斷的顯示ping。
請(qǐng)求服務(wù)http://localhost:8765/hello/rude3knife葛假,就可以看到監(jiān)控的效果了障陶,首先訪問(wèn)http://localhost:8765/hystrix.stream,顯示如下:
出錯(cuò)了聊训!
隨機(jī)谷歌了下問(wèn)題抱究,網(wǎng)友給出了問(wèn)題解決方案:
https://blog.csdn.net/ddxd0406/article/details/79643059
答主通過(guò)查看源碼后給出的解決方案是:
評(píng)論區(qū)給出更為方便且優(yōu)雅的方案:
在配置文件中加入management.endpoints.web.exposure.include=*,將端口暴露就不需要聲明bean了带斑,訪問(wèn)地址要變一下/actuator/hystrix.stream
于是我們?cè)趛ml中加入:
# 熔斷器DashBoard: actuator在boot2.0調(diào)整后開(kāi)關(guān)web端點(diǎn)的配置媳维,*代表開(kāi)啟所有
management:
endpoints:
web:
exposure:
include: "*"
重新運(yùn)行模塊,然后在網(wǎng)址一欄輸入:
http://localhost:8765/actuator/hystrix.stream
頁(yè)面會(huì)不停顯示Loading遏暴,不用慌侄刽,不顯示Unable to connect to Command Metric Stream.了。顯示Loading是因?yàn)橐驗(yàn)檫€沒(méi)有請(qǐng)求過(guò)來(lái)朋凉。
之后我們嘗試發(fā)一次請(qǐng)求州丹,在地址欄對(duì)server-feign的8765端口發(fā)送請(qǐng)求:
http://localhost:8765/hello/1232312
再來(lái)看之前的頁(yè)面,大功告成杂彭。
到此單個(gè)應(yīng)用的熔斷監(jiān)控已經(jīng)完成墓毒。
Turbine
但是只使用Hystrix Dashboard的話, 你只能看到單個(gè)應(yīng)用內(nèi)的服務(wù)信息, 這明顯不夠. 我們需要一個(gè)工具能讓我們匯總系統(tǒng)內(nèi)多個(gè)服務(wù)的數(shù)據(jù)并顯示到Hystrix Dashboard上, 這個(gè)工具就是Turbine.
在復(fù)雜的分布式系統(tǒng)中,相同服務(wù)的節(jié)點(diǎn)經(jīng)常需要部署上百甚至上千個(gè)亲怠,很多時(shí)候所计,運(yùn)維人員希望能夠把相同服務(wù)的節(jié)點(diǎn)狀態(tài)以一個(gè)整體集群的形式展現(xiàn)出來(lái),這樣可以更好的把握整個(gè)系統(tǒng)的狀態(tài)团秽。
注意:
為了演示Turbine主胧,在這里重新新建一個(gè)子模塊,名為hystrix-dashboard-turbine习勤,創(chuàng)建步驟請(qǐng)看之前的第一章踪栋。
創(chuàng)建新模塊作為單獨(dú)的監(jiān)控節(jié)點(diǎn)模塊,是因?yàn)檠菔綿ashboard的時(shí)候是耦合在了server-feign中图毕,而Turbine需要同時(shí)監(jiān)控多個(gè)服務(wù)消費(fèi)者夷都,把他耦合在一個(gè)消費(fèi)者里顯得不合時(shí)宜。
創(chuàng)建后予颤,pom.xml中引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<!--spring boot 1.X:spring-cloud-starter-hystrix-dashboard-->
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-turbine</artifactId>
</dependency>
啟動(dòng)類中@EnableTurbine和@EnableHystrixDashboard:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
@SpringBootApplication
@EnableTurbine
@EnableHystrixDashboard
public class HystrixDashboardTurbineApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardTurbineApplication.class, args);
}
}
yml中:
server:
# 服務(wù)端口號(hào)
port: 8767
spring:
application:
# 服務(wù)名囤官,即serviceId
name: hystrix-dashboard-turbine
eureka:
client:
serviceUrl:
# 安全認(rèn)證的服務(wù)注冊(cè)中心地址
defaultZone: http://localhost:8761/eureka
# 熔斷器turbine
turbine:
aggregator:
cluster-config: default
cluster-name-expression: new String("default")
app-config: SERVICE-FEIGN, SERVICE-FEIGN-2
代碼重點(diǎn):
- 新模塊端口號(hào)為:8767
- turbine.appConfig :配置Eureka中的serviceId列表,表明監(jiān)控哪些服務(wù)(這里指定為SERVICE-FEIGN, SERVICE-FEIGN-2蛤虐,稍后說(shuō))
- turbine.aggregator.clusterConfig :指定聚合哪些集群党饮,多個(gè)使用”,”分割,默認(rèn)為default笆焰〗倭拢可使用http://.../turbine.stream?cluster={clusterConfig之一}訪問(wèn)
- turbine.clusterNameExpression :
- clusterNameExpression指定集群名稱,默認(rèn)表達(dá)式appName;此時(shí):turbine.aggregator.clusterConfig需要配置想要監(jiān)控的應(yīng)用名稱捏检;
- 當(dāng)clusterNameExpression: default時(shí)荞驴,turbine.aggregator.clusterConfig可以不寫,因?yàn)槟J(rèn)就是default贯城;
- 當(dāng)clusterNameExpression: metadata[‘cluster’]時(shí)熊楼,假設(shè)想要監(jiān)控的應(yīng)用配置了eureka.instance.metadata-map.cluster: ABC,則需要配置能犯,同時(shí)turbine.aggregator.clusterConfig: ABC
完成這些步驟后鲫骗,我們還需要調(diào)整下服務(wù)提供者,我們需要兩個(gè)服務(wù)提供者同時(shí)運(yùn)行踩晶。
將8765(SERVICE-FEIGN)的服務(wù)提供者改為8766执泰,名稱改為:SERVICE-FEIGN-2,運(yùn)行起來(lái)渡蜻,別忘了在運(yùn)行設(shè)置中設(shè)置允許多個(gè)實(shí)例運(yùn)行术吝。修改后的SERVICE-FEIGN的yml是:
server:
# 服務(wù)端口號(hào)
port: 8766
spring:
application:
# 服務(wù)名,即serviceId
name: service-feign-2
eureka:
client:
serviceUrl:
# 安全認(rèn)證的服務(wù)注冊(cè)中心地址
defaultZone: http://localhost:8761/eureka
# 熔斷器設(shè)置
feign:
hystrix:
enabled: true
# 熔斷器DashBoard: actuator在boot2.0調(diào)整后開(kāi)關(guān)web端點(diǎn)的配置茸苇,*代表開(kāi)啟所有
management:
endpoints:
web:
exposure:
include: "*"
運(yùn)行后結(jié)構(gòu)如圖:
我們?cè)L問(wèn):
訪問(wèn) http://localhost:8767/turbine.stream
可以看到ping的信息流排苍,這時(shí)說(shuō)明8767正在不斷ping指定的兩個(gè)服務(wù)
進(jìn)行圖形化監(jiān)控查看,輸入:http://localhost:8767/hystrix 学密,返回酷酷的小熊界面淘衙,輸入: http://localhost:8767/turbine.stream ,然后點(diǎn)擊 Monitor Stream ,可以看到出現(xiàn)了監(jiān)控列表:
咦腻暮?為啥只有一個(gè)監(jiān)控呢彤守,說(shuō)好的聚合監(jiān)控列表呢。
這里有個(gè)坑西壮,或者說(shuō)是理解上的誤區(qū):
監(jiān)控不同的服務(wù)熔斷遗增,首先得是不同的rpc調(diào)用,也就是消費(fèi)者的熔斷函數(shù)要是兩個(gè)不同的款青,或者,消費(fèi)者去調(diào)用的是兩個(gè)不同的服務(wù)提供者霍狰!這樣才會(huì)有多個(gè)監(jiān)控表抡草。不然,有何意義蔗坯?
想明白這一步康震,你就應(yīng)該知道怎么把多個(gè)表弄出來(lái)了。
你需要讓消費(fèi)者有一個(gè):
- 新的消費(fèi)目標(biāo)(新的服務(wù)提供者)
- 或者是一個(gè)新的消費(fèi)熔斷器宾濒。
修改server-feign子模塊
- ServiceHi接口的調(diào)用變?yōu)閟ayHiFromServiceHi2腿短,value也指向另一個(gè)服務(wù)提供者service-hi-2 (新的消費(fèi)目標(biāo)(新的服務(wù)提供者))
@FeignClient(value = "service-hi-2", fallback = ServiceHiHystrix.class)
public interface ServiceHi {
/**
* <p>通過(guò)Feign偽Http客戶端調(diào)用service-hi提供的服務(wù)</p>
* @author hanchao 2018/5/19 17:59
**/
@GetMapping("/hi/{name}")
String sayHiFromServiceHi2(@PathVariable(value = "name") String name);
}
- 修改熔斷器ServiceHiHystrix (新的消費(fèi)熔斷器)
@Component
public class ServiceHiHystrix implements ServiceHi {
@Override
public String sayHiFromServiceHi2(String name) {
return "hello" + name +", this message send failed. By Hystrix.";
}
}
- 當(dāng)然,隨之更改的是HelloController,需要改為
return serviceHi.sayHiFromServiceHi2
@RestController
public class HelloController {
/** 注入服務(wù)"service-hi"的Feign客戶端ServiceHi */
@Autowired
private ServiceHi serviceHi;
/**
* 調(diào)用Feign客戶端提供的服務(wù)橘忱,自帶負(fù)載均衡
* @param name
* @return
*/
@GetMapping("/hello/{name}")
public String sayHi(@PathVariable String name){
//調(diào)用Feign客戶端ScheduleServiceHi的接口
return serviceHi.sayHiFromServiceHi2(name);
}
}
這樣改完后赴魁,重啟這個(gè)8766的服務(wù)消費(fèi)者
結(jié)構(gòu)依然不變:
我們得到了:
- 上方兩排:不同的熔斷器
- 下方兩排:不同的消費(fèi)提供者
這里確實(shí)比較難理解哈,坑了我好久钝诚。
參考
springcloud(四):熔斷器Hystrix
http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html
springcloud(五):熔斷監(jiān)控Hystrix Dashboard和Turbine
http://www.ityouknow.com/springcloud/2017/05/18/hystrix-dashboard-turbine.html