微服務(wù)架構(gòu)Springcloud詳解-SpringCloud Hystrix

Hystrix 是什么
Hystrix被稱為熔斷器,它是一個用于處理分布式系統(tǒng)的延遲和容錯的開源庫,在分布式系統(tǒng)里,許多服務(wù)之間通過遠(yuǎn)程調(diào)用實(shí)現(xiàn)信息交互,調(diào)用時不可避免會出現(xiàn)調(diào)用失敗,比如超時、異常等原因?qū)е抡{(diào)用失敗,Hystrix能夠保證在一個服務(wù)出問題的情況下,不會導(dǎo)致整體服務(wù)失敗,避免級聯(lián)故障(服務(wù)雪崩),以提高分布式系統(tǒng)的彈性;
視頻觀看戳~~~??????

熔斷器也有叫斷路器,他們表示同一個意思,最早來源于微服務(wù)之父 MartinFowler 的論文 CircuitBreaker 一文。“熔斷器”本身是一種開關(guān)裝置,用于在電路上保護(hù)線路過載,當(dāng)線路中有電器發(fā)生短路時,能夠及時切斷故障電路,防止發(fā)生過載、發(fā)熱甚至起火等嚴(yán)重后果。
所以當(dāng)某個服務(wù)單元發(fā)生故障之后,通過斷路器的故障監(jiān)控(類似熔斷保險(xiǎn)絲),向調(diào)用方返回一個符合預(yù)期的、可處理的備選響應(yīng)(FallBack),而不是長時間的等待或者拋出調(diào)用方無法處理的異常,這樣就保證了服務(wù)調(diào)用方的線程不會被長時間地占用,從而避免了故障在分布式系統(tǒng)中的蔓延,乃至雪崩。

比如:
比如電商中的用戶下訂單,我們有兩個服務(wù),一個下訂單服務(wù),一個減庫存服務(wù),當(dāng)用戶下訂單時調(diào)用下訂單服務(wù),然后下訂單服務(wù)又調(diào)用減庫存服務(wù),如果減庫存服務(wù)響應(yīng)延遲或者沒有響應(yīng),則會造成下訂單服務(wù)的線程掛起等待,如果大量的用戶請求下訂單,或?qū)е麓罅康恼埱蠖逊e,引起下訂單服務(wù)也不可用,如果還有另外一個服務(wù)依賴于訂單服務(wù),比如用戶服務(wù),它需要查詢用戶訂單,那么用戶服務(wù)查詢訂單也會引起大量的延遲和請求堆積,導(dǎo)致用戶服務(wù)也不可用。
所以在微服務(wù)架構(gòu)中,很容易造成服務(wù)故障的蔓延,引發(fā)整個微服務(wù)系統(tǒng)癱瘓不可用。
Spring Cloud Hystrix 實(shí)現(xiàn)了熔斷器、線程隔離等一系列服務(wù)保護(hù)功能。該功能也是基于Netflix 的開源框架 Hystrix 實(shí)現(xiàn)的,該框架的目標(biāo)在于通過控制那些訪問遠(yuǎn)程系統(tǒng)、服務(wù)和第三方庫的節(jié)點(diǎn),從而對延遲和故障提供更強(qiáng)大的容錯能力。?(Spring Cloud Hystrix?對 Netflix Hystrix 做了一個starter)
程序BUG,數(shù)據(jù)不匹配,響應(yīng)時間過長,服務(wù)不可用?等等.....都可能導(dǎo)致服務(wù)雪崩;
針對上面的問題,hystrix提供了?:
熔斷降級
請求限流
服務(wù)降級是指當(dāng)某個微服務(wù)響應(yīng)時間過長,發(fā)生異常,或者服務(wù)不可用了,我們不能把錯誤信息返回回來,或者讓它一直卡在那里,所以要準(zhǔn)備一個對應(yīng)的策略(一個方法),當(dāng)發(fā)生這種問題時,我們直接調(diào)用這個備用的方法來快速返回一個默認(rèn)的結(jié)果,讓請求得到快速響應(yīng),而不是一直卡在那里;
操作步驟
在 Spring Cloud 中使用熔斷器 Hystrix 是非常簡單和方便的,只需要簡單兩步即可:
1、加依賴
<!-- spring-cloud-starter-netflix-hystrix -->
<dependency>
????<groupId>org.springframework.cloud</groupId>
????<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
?2、在入口類中使用@EnableCircuitBreaker?注解或 @EnableHystrix開啟斷路器功能,也可以使用一個名為@SprinGCloudApplication?的注解代替主類上的三個注解;
3、在調(diào)用遠(yuǎn)程服務(wù)的方法上添加注解:
@HystrixCommand(fallbackMethod = "fallback")
hystrix 默認(rèn)超時時間是 1000 毫秒,如果你后端的響應(yīng)超過此時間,就會觸發(fā)斷路器;
修改 hystrix 的默認(rèn)超時時間:
@RequestMapping("/cloud/goodsHystrix")
@HystrixCommand(fallbackMethod = "fallback",
????????????????commandProperties={
????????????????????????@HystrixProperty(name="execution.timeout.enabled", value="true"),
????????????????????@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="5000")
????????????????})
public ResultObject goodsHystrix()
或者在配置文件進(jìn)行配置:
ribbon.ReadTimeout=6000
ribbon.ConnectTimeout=3000
hystrix.command.default.execution.timeout.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
這里有個坑需要注意一下:
如果hystrix.command.default.execution.timeout.enabled為true,則會有兩個執(zhí)行方法超時的配置,一個就是ribbon的ReadTimeout,一個就是熔斷器hystrix的timeoutInMilliseconds, 此時誰的值小誰生效;
如果hystrix.command.default.execution.timeout.enabled為false,則熔斷器不進(jìn)行超時熔斷,而是根據(jù)ribbon的ReadTimeout拋出的異常而熔斷,也就是取決于ribbon的ConnectTimeout,配置的是請求服務(wù)的超時時間,除非服務(wù)找不到,或者網(wǎng)絡(luò)原因,這個時間才會生效;
ribbon.ReadTimeout=6000
ribbon.ConnectTimeout=3000
Hystrix的異常處理
我們在調(diào)用服務(wù)提供者時,服務(wù)提供者可能拋出異常,我們自己也可能拋異常,默認(rèn)情況下方法拋了異常會自動進(jìn)行服務(wù)降級,交給服務(wù)降級中的方法去處理;
當(dāng)我們自己發(fā)生異常后,只需要在服務(wù)降級方法中添加一個 Throwable 類型的
參數(shù)就能夠獲取到拋出的異常的類型,如下:
public ResultObject fallback(Throwable throwable) {
????System.out.println(throwable.getMessage());
????return new ResultObject(Constant.ONE,"服務(wù)降級");
}
當(dāng)然遠(yuǎn)程服務(wù)發(fā)生了異常也可以獲取到異常信息;
如果遠(yuǎn)程服務(wù)有一個異常拋出后我們不希望進(jìn)入到服務(wù)降級方法中去處理,而是
直接將異常拋給用戶,那么我們可以在@HystrixCommand 注解中添加忽略異
常,如下:
@HystrixCommand(fallbackMethod = "fallback", ignoreExceptions=Throwable.class,
降級是作用??
1、可以監(jiān)聽你的請求有沒有超時;(默認(rèn)是1秒,時間可以改)
2、異?;驁?bào)錯了可以快速讓請求返回,不會一直等待;(避免線程累積)
3、當(dāng)?shù)南到y(tǒng)馬上迎來大量的并發(fā)(雙十一秒殺這種或者促銷活動) ?此時如果系統(tǒng)承載不了這么大的并發(fā)時,可以考慮先關(guān)閉一些不重要的微服務(wù)(在降級方法中返回一個比較友好的信息),把資源讓給核心微服務(wù),待高峰流量過去,再開啟回來。
Hystrix限流
限流有很多方案:
1、Nginx
2、Redis + Lua
3、Sentinel
4、基于限流算法自己實(shí)現(xiàn)(令牌桶、漏桶算法)
hystrix限流就是限制你某個微服務(wù)的使用量(可用線程數(shù)、信號量)
hystrix通過線程池的方式來管理微服務(wù)的調(diào)用,它默認(rèn)是一個線程池(大小10個) 管理你的所有微服務(wù),你可以給某個微服務(wù)開辟新的線程池:
@RequestMapping("/cloud/goodsHystrix2")
@HystrixCommand(fallbackMethod = "fallback",
????????????????threadPoolKey = "goods",
????????????????threadPoolProperties = {@HystrixProperty(name = "coreSize", value = "2"),
????????????????@HystrixProperty(name = "maxQueueSize", value = "1")})
public ResultObject goodsHystrix2() throws InterruptedException {
threadPoolKey 是線程池唯一標(biāo)識, hystrix 會使用該標(biāo)識來計(jì)數(shù),看線程占用是否超過了, 超過了就會直接降級該次調(diào)用;
這里coreSize給他值為2 那么假設(shè)你這個方法調(diào)用時間是1s執(zhí)行完, 那么在1s內(nèi)如果有超過2個請求進(jìn)來的話,剩下的請求則全部降級;
其中maxQueueSize是一個線程隊(duì)列,里面只能放一個請求線程,本來線程數(shù)有2個,隊(duì)列里面允許放一個,那么總共只能有3個請求線程執(zhí)行,如果超過了就會限流;
feign整合hystrix
feign 默認(rèn)是支持hystrix的, 但是在Spring cloud Dalston 版本之后就默認(rèn)關(guān)閉了, 因?yàn)闃I(yè)務(wù)需求不一定要使用;
所以現(xiàn)在要使用首先得打開他,在yml文件加上如下配置:
feign.hystrix.enabled=true
加上配置之后降級方法怎么寫呢?
@FeignClient(value="34-SPRINGCLOUD-SERVICE-GOODS", fallback = GoodsRemoteClientFallBack.class)
public interface GoodsRemoteClient {
????/**
?????* 聲明一個feign的接口,它的實(shí)現(xiàn)是服務(wù)提供者的controller實(shí)現(xiàn)
?????*
?????* @return
?????*/
????@RequestMapping("/service/goods")
????public ResultObject goods();
}
在feign客戶端的注解上 有個屬性叫fallback 然后指向一個類
GoodsRemoteClientFallBack類:
@Component
public class GoodsRemoteClientFallBack implements GoodsRemoteClient {
????@Override
????public ResultObject goods() {
????????return new ResultObject(Constant.ONE,"feign服務(wù)調(diào)用降級");
????}
}
如此方法降級便可以了;
當(dāng)然如果需要拿到具體的服務(wù)錯誤信息,那么可以這樣:
客戶端指定一個fallbackFactory即可;
@FeignClient(value="34-SPRINGCLOUD-SERVICE-GOODS", fallbackFactory = GoodsRemoteClientFallBackFactory.class)
public interface GoodsRemoteClient {
????/**
?????* 聲明一個feign的接口,它的實(shí)現(xiàn)是服務(wù)提供者的controller實(shí)現(xiàn)
?????*
?????* @return
?????*/
????@RequestMapping("/service/goods")
????public ResultObject goods();
}@Component
public class GoodsRemoteClientFallBackFactory implements FallbackFactory<GoodsRemoteClient> {
????@Override
????public GoodsRemoteClient create(Throwable throwable) {
????????return new GoodsRemoteClient() {
????????????@Override
????????????public ResultObject goods() {
????????????????String message = throwable.getMessage();
????????????????System.out.println("feign遠(yuǎn)程調(diào)用異常:" + message);
????????????????return new ResultObject();
????????????}
????????};
????}
}
這個message 就是錯誤信息,至此,就完成了feign與hystrix的整合;
Spring?Cloud?Feign超時時間設(shè)置
Feign調(diào)用服務(wù)的默認(rèn)時長是1秒鐘,也就是如果超過1秒沒連接上或者超過1秒沒響應(yīng),那么會相應(yīng)的報(bào)錯。而實(shí)際情況是因?yàn)闃I(yè)務(wù)的不同可能出現(xiàn)超出1秒的情況,這時我們需要調(diào)整超時時間。
Feign 的負(fù)載均衡底層用的就是?Ribbon在application.properties中添加如下配置,超過5秒沒連接上報(bào)連接超時,如果超過5秒沒有響應(yīng),報(bào)請求超時;
# 參考RibbonClientConfiguration
# 請求連接的超時時間 默認(rèn)的時間為?1 秒
ribbon.ConnectTimeout=5000
# 請求處理的超時時間
ribbon.ReadTimeout=5000
ribbon還有MaxAutoRetries對當(dāng)前實(shí)例的重試次數(shù),
MaxAutoRetriesNextServer對切換實(shí)例的重試次數(shù),
如果ribbon的ReadTimeout超時,或者ConnectTimeout連接超時,會進(jìn)行重試操作
由于ribbon的重試機(jī)制,通常熔斷hystrix的超時時間需要配置的比ReadTimeou長,ReadTimeout比ConnectTimeout長,否則還未重試,就熔斷了;
為了確保重試機(jī)制的正常運(yùn)作,理論上(以實(shí)際情況為準(zhǔn))建議hystrix的超時時間為:(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout
hystrix相關(guān)配置:
Execution相關(guān)的屬性的配置
hystrix.command.default.execution.isolation.strategy?隔離策略,默認(rèn)是Thread,?可選Thread|Semaphore(信號量)
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds?命令執(zhí)行超時時間,默認(rèn)1000ms
hystrix.command.default.execution.timeout.enabled?執(zhí)行是否啟用超時,默認(rèn)啟用true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout?發(fā)生超時是是否中斷,默認(rèn)true
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests?最大并發(fā)請求數(shù),默認(rèn)10,該參數(shù)當(dāng)使用ExecutionIsolationStrategy.SEMAPHORE策略時才有效。如果達(dá)到最大并發(fā)請求數(shù),請求會被拒絕。理論上選擇semaphore?size的原則和選擇thread?size一致,但選用semaphore時每次執(zhí)行的單元要比較小且執(zhí)行速度快(ms級別),否則的話應(yīng)該用thread。semaphore應(yīng)該占整個容器(tomcat)的線程池的一小部分。 Fallback相關(guān)的屬性 這些參數(shù)可以應(yīng)用于Hystrix的THREAD和SEMAPHORE策略;
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequest如果并發(fā)數(shù)達(dá)到該設(shè)置值,請求會被拒絕和拋出異常并且fallback不會被調(diào)用。默認(rèn)10
hystrix.command.default.fallback.enabled?當(dāng)執(zhí)行失敗或者請求被拒絕,是否會嘗試調(diào)用hystrixCommand.getFallback()?。默認(rèn)true
Circuit?Breaker相關(guān)的屬性
hystrix.command.default.circuitBreaker.enabled?用來跟蹤circuit的健康性,如果未達(dá)標(biāo)則讓request短路。默認(rèn)true
hystrix.command.default.circuitBreaker.requestVolumeThreshold?一個rolling?window內(nèi)最小的請求數(shù)。如果設(shè)為20,那么當(dāng)一個rolling?window的時間內(nèi)(比如說1個rolling?window是10秒)收到19個請求, 即使19個請求都失敗,也不會觸發(fā)circuit?break。默認(rèn)20
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds?觸發(fā)短路的時間值,當(dāng)該值設(shè)為5000時,則當(dāng)觸發(fā)circuit?break后的5000毫秒內(nèi)都會拒絕request,也就是5000毫秒后才會關(guān)閉circuit。 默認(rèn)5000
hystrix.command.default.circuitBreaker.errorThresholdPercentage錯誤比率閥值,如果錯誤率>=該 值,circuit會被打開,并短路所有請求觸發(fā)fallback。默認(rèn)50
hystrix.command.default.circuitBreaker.forceOpen?強(qiáng)制打開熔斷器,如果打開這個開關(guān),那么拒絕所 有request,默認(rèn)false
hystrix.command.default.circuitBreaker.forceClosed?強(qiáng)制關(guān)閉熔斷器?如果這個開關(guān)打開,circuit將 一直關(guān)閉且忽略circuitBreaker.errorThresholdPercentage
Metrics相關(guān)參數(shù)
hystrix.command.default.metrics.rollingStats.timeInMilliseconds?設(shè)置統(tǒng)計(jì)的時間窗口值的,毫秒值,circuit?break?的打開會根據(jù)1個rolling?window的統(tǒng)計(jì)來計(jì)算。若rolling?window被設(shè)為10000毫秒, 則rolling?window會被分成n個buckets,每個bucket包含success,failure,timeout,rejection的次數(shù)的統(tǒng)計(jì)信息。默認(rèn)10000
hystrix.command.default.metrics.rollingStats.numBuckets?設(shè)置一個rolling?window被劃分的數(shù) 量,若numBuckets=10,rolling?window=10000,那么一個bucket的時間即1秒。必須符合rolling?window? %?numberBuckets?==?0。默認(rèn)10
hystrix.command.default.metrics.rollingPercentile.enabled?執(zhí)行時是否enable指標(biāo)的計(jì)算和跟蹤, 默認(rèn)true
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds?設(shè)置rolling? percentile?window的時間,默認(rèn)60000
hystrix.command.default.metrics.rollingPercentile.numBuckets?設(shè)置rolling?percentile? window的numberBuckets。邏輯同上。默認(rèn)6
hystrix.command.default.metrics.rollingPercentile.bucketSize?如果bucket?size=100,window =10s,若這10s里有500次執(zhí)行,只有最后100次執(zhí)行會被統(tǒng)計(jì)到bucket里去。增加該值會增加內(nèi)存開銷以及排序 的開銷。默認(rèn)100
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds?記錄health?快照(用 來統(tǒng)計(jì)成功和錯誤綠)的間隔,默認(rèn)500ms
Request?Context?相關(guān)參數(shù)
hystrix.command.default.requestCache.enabled?默認(rèn)true,需要重載getCacheKey(),返回null時不 緩存hystrix.command.default.requestLog.enabled?記錄日志到HystrixRequestLog,默認(rèn)true
Collapser?Properties?相關(guān)參數(shù)
hystrix.collapser.default.maxRequestsInBatch?單次批處理的最大請求數(shù),達(dá)到該數(shù)量觸發(fā)批處理,默認(rèn) Integer.MAX_VALU
hystrix.collapser.default.timerDelayInMilliseconds?觸發(fā)批處理的延遲,也可以為創(chuàng)建批處理的時間 +該值,默認(rèn)10hystrix.collapser.default.requestCache.enabled?是否對HystrixCollapser.execute()?and? HystrixCollapser.queue()的cache,默認(rèn)true
ThreadPool?相關(guān)參數(shù)
線程數(shù)默認(rèn)值10適用于大部分情況(有時可以設(shè)置得更?。?,如果需要設(shè)置得更大,那有個基本得公式可以 follow: requests?per?second?at?peak?when?healthy?×?99th?percentile?latency?in?seconds?+?some? breathing?room 每秒最大支撐的請求數(shù)?(99%平均響應(yīng)時間?+?緩存值) 比如:每秒能處理1000個請求,99%的請求響應(yīng)時間是60ms,那么公式是: 1000?(0.060+0.012)基本得原則時保持線程池盡可能小,他主要是為了釋放壓力,防止資源被阻塞。 當(dāng)一切都是正常的時候,線程池一般僅會有1到2個線程激活來提供服務(wù)hystrix.threadpool.default.coreSize?并發(fā)執(zhí)行的最大線程數(shù),默認(rèn)10
hystrix.threadpool.default.maxQueueSize?BlockingQueue的最大隊(duì)列數(shù),當(dāng)設(shè)為-1,會使用SynchronousQueue,值為正時使用LinkedBlcokingQueue。該設(shè)置只會在初始化時有效,之后不能修改threadpool的queue?size,除非reinitialising?thread?executor。默認(rèn)-1。
hystrix.threadpool.default.queueSizeRejectionThreshold?即使maxQueueSize沒有達(dá)到,達(dá)到 queueSizeRejectionThreshold該值后,請求也會被拒絕。因?yàn)閙axQueueSize不能被動態(tài)修改,這個參數(shù)將允 許我們動態(tài)設(shè)置該值。if?maxQueueSize?==?1,該字段將不起作用 hystrix.threadpool.default.keepAliveTimeMinutes?如果corePoolSize和maxPoolSize設(shè)成一樣(默認(rèn) 實(shí)現(xiàn))該設(shè)置無效。如果通過plugin(https://github.com/Netflix/Hystrix/wiki/Plugins)使用自定義 實(shí)現(xiàn),該設(shè)置才有用,默認(rèn)1.
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds?線程池統(tǒng)計(jì)指標(biāo)的時間,默 認(rèn)10000
hystrix.threadpool.default.metrics.rollingStats.numBuckets?將rolling?window劃分為n個 buckets,默認(rèn)10;
最后,奉上sprinGCloud視頻教程,視頻學(xué)習(xí)效果更佳,走過路過別忘素質(zhì)三連哦~~

