Hystrix是一個(gè)用于處理分布式系統(tǒng)的延遲和容錯(cuò)的開源庫宋欺,可是實(shí)現(xiàn)服務(wù)降級(jí)艺挪,服務(wù)熔斷,服務(wù)限流等功能鸿染,目前已經(jīng)停止更新指蚜,進(jìn)入維護(hù)
依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
服務(wù)降級(jí)
當(dāng)某個(gè)服務(wù)出現(xiàn)異常或者這長時(shí)間無響應(yīng)牡昆,則會(huì)使調(diào)用此服務(wù)的其他服務(wù)造成不好的影響姚炕,返回的信息也不友好摊欠。服務(wù)降級(jí)就是解決這個(gè)問題,當(dāng)一個(gè)服務(wù)超出響應(yīng)時(shí)間或者異常柱宦,則會(huì)執(zhí)行事先設(shè)計(jì)好的方法來‘兜底’
使用前些椒,要在主程序上加上@EnableHystrix(里邊包括了@EnableCircuitBreaker)注解開啟功能
hystrix注解開發(fā)使用很方便,如下
@Service
public class PaymentService {
// 出現(xiàn)問題調(diào)用的方法
@HystrixCommand(fallbackMethod = "paymentInfoTimeOutHandle",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000") // 服務(wù)降級(jí)時(shí)間
})
public String paymentInfoTimeOut(Integer id) {
try { TimeUnit.MILLISECONDS.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
return "線程池: "+Thread.currentThread().getName()+" id: "+id;
}
public String paymentInfoTimeOutHandle(Integer id) {
return "線程池: "+Thread.currentThread().getName()+" id: "+id+"\t 系統(tǒng)繁忙 o(╥﹏╥)o";
}
}
paymentInfoTimeOut方法超時(shí)或拋出異常掸刊,則會(huì)執(zhí)行fallbackMethod指定
與feign集成的使用
配置文件加入
feign:
hystrix:
enabled: true
這個(gè)配置的作用是:Feign將使用斷路器包裝所有方法免糕,也就是將@FeignClient標(biāo)記的那個(gè)service接口下所有的方法進(jìn)行了hystrix包裝(似于在這些方法上加了一個(gè)@HystrixCommand),這些方法會(huì)有一個(gè)1s的默認(rèn)超時(shí)時(shí)間忧侧,超過就會(huì)fallback石窑,想要修改的話可以在yml中配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000 #換成了三秒
集成使用:
@Component
@FeignClient(value = "payment",fallback = FeignHystrixService.class)
public interface FeignService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
fallback 指定一個(gè)FeignService 接口的實(shí)現(xiàn)類,每個(gè)方法的實(shí)現(xiàn)就是默認(rèn)的兜底方法蚓炬,如下
@Component
public class FeignHystrixService implements FeignService{
@Override
public String paymentInfo_OK(Integer id) {
return "超時(shí)了松逊。。肯夏。o(╥﹏╥)o";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "超時(shí)了经宏。。驯击。o(╥﹏╥)o";
}
}
服務(wù)熔斷
熔斷機(jī)制是應(yīng)對(duì)雪崩效應(yīng)的一種微服務(wù)鏈路保護(hù)機(jī)制,當(dāng)一段時(shí)間內(nèi)烁兰,服務(wù)降級(jí)觸發(fā)的次數(shù)超過一定百分比,就會(huì)觸發(fā)熔斷徊都,熔斷期間沪斟,所有的請(qǐng)求無論是否超時(shí)或異常,都會(huì)走降級(jí)方法暇矫,經(jīng)過規(guī)定窗口期后主之,hystrix會(huì)自動(dòng)處理一些請(qǐng)求做嘗試,然后自動(dòng)恢復(fù)
使用
@HystrixCommand(fallbackMethod = "paymentInfoTimeOutHandle",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否開啟斷路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 請(qǐng)求次數(shù)
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 時(shí)間窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失敗率達(dá)到多少后跳閘
})
public String paymentCircuitBreaker(Integer id) {
if(id < 0) {
throw new RuntimeException("id 不能負(fù)數(shù)");
}
String serialNumber = UUID.randomUUID().toString();
return Thread.currentThread().getName()+"\t"+"調(diào)用成功袱耽,流水號(hào): " + serialNumber;
}
public String paymentInfoTimeOutHandle(Integer id) {
return "線程池: "+Thread.currentThread().getName()+" id: "+id+"\t 系統(tǒng)繁忙 o(╥﹏╥)o";
}
之后可以啟動(dòng)服務(wù)試一下杀餐,傳入-1參數(shù)多次,會(huì)觸發(fā)熔斷朱巨,但在此輸入正數(shù)id依然會(huì)調(diào)用降級(jí)方法,之后會(huì)自動(dòng)恢復(fù)
關(guān)于HystrixProperty的配置
@HystrixCommand(fallbackMethod = "fallbackMethod",
groupKey = "strGroupCommand",
commandKey = "strCommand",
threadPoolKey = "strThreadPool",
commandProperties = {
// 設(shè)置隔離策略枉长,THREAD 表示線程池 SEMAPHORE:信號(hào)池隔離
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
// 當(dāng)隔離策略選擇信號(hào)池隔離的時(shí)候冀续,用來設(shè)置信號(hào)池的大小(最大并發(fā)數(shù))
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 配置命令執(zhí)行的超時(shí)時(shí)間
@HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
// 是否啟用超時(shí)時(shí)間
@HystrixProperty(name = "execution.timeout.enabled", value = "true"),
// 執(zhí)行超時(shí)的時(shí)候是否中斷
@HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
// 執(zhí)行被取消的時(shí)候是否中斷
@HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
// 允許回調(diào)方法執(zhí)行的最大并發(fā)數(shù)
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 服務(wù)降級(jí)是否啟用必峰,是否執(zhí)行回調(diào)函數(shù)
@HystrixProperty(name = "fallback.enabled", value = "true"),
// 是否啟用斷路器
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 該屬性用來設(shè)置在滾動(dòng)時(shí)間窗中洪唐,斷路器熔斷的最小請(qǐng)求數(shù)。例如吼蚁,默認(rèn)該值為 20 的時(shí)候凭需,如果滾動(dòng)時(shí)間窗(默認(rèn)10秒)內(nèi)僅收到了19個(gè)請(qǐng)求问欠, 即使這19個(gè)請(qǐng)求都失敗了,斷路器也不會(huì)打開粒蜈。
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
// 該屬性用來設(shè)置在滾動(dòng)時(shí)間窗中顺献,表示在滾動(dòng)時(shí)間窗中,在請(qǐng)求數(shù)量超過 circuitBreaker.requestVolumeThreshold 的情況下枯怖,如果錯(cuò)誤請(qǐng)求數(shù)的百分比超過50, 就把斷路器設(shè)置為 "打開" 狀態(tài)注整,否則就設(shè)置為 "關(guān)閉" 狀態(tài)。
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
// 該屬性用來設(shè)置當(dāng)斷路器打開之后的休眠時(shí)間窗度硝。 休眠時(shí)間窗結(jié)束之后肿轨,會(huì)將斷路器置為 "半開" 狀態(tài),嘗試熔斷的請(qǐng)求命令蕊程,如果依然失敗就將斷路器繼續(xù)設(shè)置為 "打開" 狀態(tài)椒袍,如果成功就設(shè)置為 "關(guān)閉" 狀態(tài)。
@HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
// 斷路器強(qiáng)制打開
@HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
// 斷路器強(qiáng)制關(guān)閉
@HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
// 滾動(dòng)時(shí)間窗設(shè)置藻茂,該時(shí)間用于斷路器判斷健康度時(shí)需要收集信息的持續(xù)時(shí)間
@HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
// 該屬性用來設(shè)置滾動(dòng)時(shí)間窗統(tǒng)計(jì)指標(biāo)信息時(shí)劃分"桶"的數(shù)量驹暑,斷路器在收集指標(biāo)信息的時(shí)候會(huì)根據(jù)設(shè)置的時(shí)間窗長度拆分成多個(gè) "桶" 來累計(jì)各度量值,每個(gè)"桶"記錄了一段時(shí)間內(nèi)的采集指標(biāo)捌治。
// 比如 10 秒內(nèi)拆分成 10 個(gè)"桶"收集這樣岗钩,所以 timeinMilliseconds 必須能被 numBuckets 整除。否則會(huì)拋異常
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
// 該屬性用來設(shè)置對(duì)命令執(zhí)行的延遲是否使用百分位數(shù)來跟蹤和計(jì)算肖油。如果設(shè)置為 false, 那么所有的概要統(tǒng)計(jì)都將返回 -1兼吓。
@HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
// 該屬性用來設(shè)置百分位統(tǒng)計(jì)的滾動(dòng)窗口的持續(xù)時(shí)間,單位為毫秒森枪。
@HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
// 該屬性用來設(shè)置百分位統(tǒng)計(jì)滾動(dòng)窗口中使用 “ 桶 ”的數(shù)量视搏。
@HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
// 該屬性用來設(shè)置在執(zhí)行過程中每個(gè) “桶” 中保留的最大執(zhí)行次數(shù)。如果在滾動(dòng)時(shí)間窗內(nèi)發(fā)生超過該設(shè)定值的執(zhí)行次數(shù)县袱,
// 就從最初的位置開始重寫浑娜。例如,將該值設(shè)置為100, 滾動(dòng)窗口為10秒式散,若在10秒內(nèi)一個(gè) “桶 ”中發(fā)生了500次執(zhí)行筋遭,
// 那么該 “桶” 中只保留 最后的100次執(zhí)行的統(tǒng)計(jì)。另外暴拄,增加該值的大小將會(huì)增加內(nèi)存量的消耗漓滔,并增加排序百分位數(shù)所需的計(jì)算時(shí)間。
@HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
// 該屬性用來設(shè)置采集影響斷路器狀態(tài)的健康快照(請(qǐng)求的成功乖篷、 錯(cuò)誤百分比)的間隔等待時(shí)間响驴。
@HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
// 是否開啟請(qǐng)求緩存
@HystrixProperty(name = "requestCache.enabled", value = "true"),
// HystrixCommand的執(zhí)行和事件是否打印日志到 HystrixRequestLog 中
@HystrixProperty(name = "requestLog.enabled", value = "true"),
},
threadPoolProperties = {
// 該參數(shù)用來設(shè)置執(zhí)行命令線程池的核心線程數(shù),該值也就是命令執(zhí)行的最大并發(fā)量
@HystrixProperty(name = "coreSize", value = "10"),
// 該參數(shù)用來設(shè)置線程池的最大隊(duì)列大小撕蔼。當(dāng)設(shè)置為 -1 時(shí)豁鲤,線程池將使用 SynchronousQueue 實(shí)現(xiàn)的隊(duì)列秽誊,否則將使用 LinkedBlockingQueue 實(shí)現(xiàn)的隊(duì)列。
@HystrixProperty(name = "maxQueueSize", value = "-1"),
// 該參數(shù)用來為隊(duì)列設(shè)置拒絕閾值琳骡。 通過該參數(shù)锅论, 即使隊(duì)列沒有達(dá)到最大值也能拒絕請(qǐng)求。
// 該參數(shù)主要是對(duì) LinkedBlockingQueue 隊(duì)列的補(bǔ)充,因?yàn)?LinkedBlockingQueue 隊(duì)列不能動(dòng)態(tài)修改它的對(duì)象大小日熬,而通過該屬性就可以調(diào)整拒絕請(qǐng)求的隊(duì)列大小了棍厌。
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
}
)
public String doSomething() {
...
}