Springcloud之Hystrix服務(wù)降級(jí)
服務(wù)雪崩
多個(gè)微服務(wù)之間調(diào)用時(shí)微酬,如果鏈路上某個(gè)微服務(wù)A的調(diào)用響應(yīng)時(shí)間過(guò)長(zhǎng)或者不可用,調(diào)用方就會(huì)占用越來(lái)越多的系統(tǒng)資源厅缺,進(jìn)而引起服務(wù)雪崩重抖。
對(duì)高流量的應(yīng)用,比失敗更糟糕的是踪古,應(yīng)用程序還可能導(dǎo)致服務(wù)之間的延遲增加含长,備份隊(duì)列,線(xiàn)程和其他資源緊張伏穆,導(dǎo)致整個(gè)系統(tǒng)發(fā)生更多的級(jí)聯(lián)故障拘泞。因此,需要對(duì)故障和延遲進(jìn)行隔離和管理枕扫,以便單個(gè)依賴(lài)關(guān)系失敗陪腌,不會(huì)影響整個(gè)服務(wù)。
是什么
用于服務(wù)降級(jí)(fallback),服務(wù)熔斷(break)诗鸭,服務(wù)限流(flowlimit)染簇,服務(wù)隔離,還有一個(gè)近實(shí)時(shí)的監(jiān)控强岸。
hystrix既可以隔離依賴(lài)服務(wù)的調(diào)用锻弓,還提供了準(zhǔn)實(shí)時(shí)的調(diào)用監(jiān)控(Hystrix Dashboard),Hystrix會(huì)持續(xù)地記錄所有通過(guò)Hystrix發(fā)起的請(qǐng)求的執(zhí)行信息请唱,并圖表展示弥咪,包括每秒執(zhí)行了多少請(qǐng)求,多少成功十绑,多少失敗等聚至。
服務(wù)降級(jí)
服務(wù)降級(jí)即兜底方法fallback,有以下幾種途徑會(huì)產(chǎn)生服務(wù)降級(jí)本橙,即超時(shí)扳躬,異常,服務(wù)宕機(jī)甚亭,線(xiàn)程池或信號(hào)量打滿(mǎn)等贷币。
服務(wù)端配置
啟動(dòng)類(lèi)增加@EnableCircuitBreaker
注解
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class CloudProviderHystrixPayment8001 {
public static void main(String[] args) {
SpringApplication.run(CloudProviderHystrixPayment8001.class, args);
}
}
通過(guò)@HystrixCommand
注解,指明fallbackMethod方法和超時(shí)時(shí)間限制亏狰。超過(guò)時(shí)間后役纹,則調(diào)用fallbackMethod指定的方法,fallbackMethod必須與被注解的函數(shù)具有相同的函數(shù)簽名暇唾。
@HystrixCommand(fallbackMethod = "paymentInfo_timeoutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000") })
public String paymentInfoTimeout(Integer id) {
log.info("{}", Thread.currentThread().getName()+" running ...");
Integer timeout = 3;
try {
TimeUnit.SECONDS.sleep(timeout);
} catch (InterruptedException e) {
// e.printStackTrace();
}
log.info("{}", " run over...");
return "線(xiàn)程池: " + Thread.currentThread().getName() + " paymentInfo_timeout, id:" + id;
}
public String paymentInfo_timeoutHandler(Integer id) {
log.info("hystrix超時(shí)處理...");
return "線(xiàn)程池: " + Thread.currentThread().getName() + " System busy, id:" + id;
}
客戶(hù)端配置
客戶(hù)端啟動(dòng)類(lèi)配置@EnableHystrix
@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class CloudConsumerFeignHystrixOrder80 {
public static void main(String[] args) {
SpringApplication.run(CloudConsumerFeignHystrixOrder80.class, args);
}
}
配置文件開(kāi)啟hystrix支持
```feign:
client:
config:
default:
connect-timeout: 5000
read-timeout: 5000
logger-level: full
compression:
request:
enabled: false
response:
enabled: false
hystrix:
enabled: true
客戶(hù)端配置促脉,單獨(dú)在函數(shù)上配置fallback,并指定超時(shí)時(shí)間
@RestController
@RequestMapping(value="/consumer")
@Slf4j
public class OrderFeignHystrixController {
@Autowired
private PaymentFeignHystrixService paymentFeignService;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfoOk(@PathVariable("id") Integer id) {
String result = paymentFeignService.paymentInfoOk(id);
log.info("******result:"+result);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeoutFallbackMethod", commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="1500")
})
public String paymentInfoTimeout(@PathVariable("id") Integer id) {
String result = paymentFeignService.paymentInfoTimeout(id);
log.info("*******result:"+result);
return result;
}
public String paymentTimeoutFallbackMethod(@PathVariable("id") Integer id) {
return "等不及了, 快上車(chē)";
}
}
客戶(hù)端配置策州,全局fallback瘸味。通過(guò)@DefaultProperties
注解的defaultFallback
屬性
@RestController
@RequestMapping(value="/consumer")
@Slf4j
@DefaultProperties(defaultFallback="payment_global_fallbackmethod")
public class OrderFeignHystrixController {
@Autowired
private PaymentFeignHystrixService paymentFeignService;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfoOk(@PathVariable("id") Integer id) {
String result = paymentFeignService.paymentInfoOk(id);
log.info("******result:"+result);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
@HystrixCommand
public String paymentInfoTimeout(@PathVariable("id") Integer id) {
String result = paymentFeignService.paymentInfoTimeout(id);
log.info("*******result:"+result);
return result;
}
public String payment_global_fallbackmethod() {
log.info("global fallback method...");
return "global異常處理信息,請(qǐng)稍后再試...";
}
}
上述方式還不夠極簡(jiǎn)够挂,比如fallback函數(shù)與函數(shù)耦合在一起旁仿,可以進(jìn)一步解耦。通過(guò)實(shí)現(xiàn)@FeignClient
的接口孽糖,指明fallback類(lèi)枯冈。其中每個(gè)函數(shù)對(duì)應(yīng)的就是fallback函數(shù)。
@Service
public class PaymentFeginHystrixFallbackService implements PaymentFeignHystrixService {
@Override
public String paymentInfoOk(Integer id) {
return "fall back, ok...";
}
@Override
public String paymentInfoTimeout(Integer id) {
return "fall back, timeout...";
}
}
在@FeignClient
的接口上办悟,指明fallback類(lèi)尘奏。這樣便可實(shí)現(xiàn),fallback函數(shù)與函數(shù)的進(jìn)一步解耦誉尖。
@Service
@FeignClient(value="cloud-payment-hystrix-service", fallback=PaymentFeginHystrixFallbackService.class)
public interface PaymentFeignHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfoOk(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfoTimeout(@PathVariable("id") Integer id);
}
服務(wù)熔斷
類(lèi)比保險(xiǎn)絲罪既,分為三個(gè)步驟:服務(wù)降級(jí)->進(jìn)而熔斷->恢復(fù)調(diào)用鏈路。
熔斷機(jī)制是應(yīng)對(duì)雪崩效應(yīng)的一種微服務(wù)鏈路保護(hù)機(jī)制。當(dāng)扇出鏈路的某個(gè)微服務(wù)出錯(cuò)不可用或者響應(yīng)時(shí)間太長(zhǎng)時(shí)琢感,會(huì)進(jìn)行服務(wù)的降級(jí)丢间,進(jìn)而熔斷該節(jié)點(diǎn)微服務(wù)的調(diào)用,快速返回錯(cuò)誤的響應(yīng)信息驹针。當(dāng)檢測(cè)到該節(jié)點(diǎn)微服務(wù)調(diào)用響應(yīng)正常后烘挫,恢復(fù)調(diào)用鏈路。
SpringCloud中柬甥,熔斷機(jī)制是通過(guò)Hystrix實(shí)現(xiàn)饮六。Hystrix會(huì)監(jiān)控微服務(wù)間的調(diào)用情況,當(dāng)失敗的調(diào)用到一定閾值苛蒲,默認(rèn)是5s內(nèi)20次調(diào)用失敗卤橄,就會(huì)啟動(dòng)熔斷機(jī)制。熔斷機(jī)制的注解是通過(guò)@HystrixCommand實(shí)現(xiàn)的臂外。
熔斷類(lèi)型:
- 熔斷打開(kāi):請(qǐng)求不在進(jìn)行調(diào)用當(dāng)前服務(wù)窟扑,內(nèi)部設(shè)置時(shí)鐘一般為MTTR(平均故障處理時(shí)間),當(dāng)打開(kāi)時(shí)長(zhǎng)達(dá)到所設(shè)時(shí)鐘則進(jìn)入半熔斷狀態(tài)漏健。
- 熔斷關(guān)閉:正常情況嚎货。
- 熔斷半開(kāi):部分請(qǐng)求根據(jù)規(guī)則調(diào)用當(dāng)前服務(wù),如果請(qǐng)求成功且符合規(guī)則則認(rèn)為當(dāng)前服務(wù)恢復(fù)正常蔫浆,關(guān)閉熔斷殖属。
涉及斷路器的三個(gè)重要參數(shù):快照時(shí)間窗,請(qǐng)求總數(shù)閾值瓦盛,錯(cuò)誤百分比閾值洗显。
- 快照時(shí)間窗:斷路器確定是否打開(kāi)需要統(tǒng)計(jì)一些請(qǐng)求和錯(cuò)誤數(shù)據(jù),統(tǒng)計(jì)的時(shí)間范圍就是快照時(shí)間窗谭溉,默認(rèn)是10s墙懂。
- 請(qǐng)求總數(shù)閾值:在快照時(shí)間窗內(nèi)橡卤,必須滿(mǎn)足請(qǐng)求總數(shù)閾值才有資格熔斷扮念。默認(rèn)是20,意味著10s內(nèi)碧库,如果Hystrix命令的調(diào)用次數(shù)不足20次柜与,即使所有的請(qǐng)求都超時(shí)或其他原因,斷路器都不會(huì)打開(kāi)嵌灰。
- 錯(cuò)誤百分比閾值:當(dāng)請(qǐng)求總數(shù)在快照時(shí)間窗內(nèi)超過(guò)了閾值弄匕,比如發(fā)生了30次調(diào)用,如果再30次調(diào)用中沽瞭,有16次發(fā)送了超時(shí)異常迁匠,則超過(guò)了50%的錯(cuò)誤百分比,在默認(rèn)設(shè)定50%閾值的情況下,這時(shí)候會(huì)將斷路器打開(kāi)城丧。
原來(lái)的主邏輯要如何恢復(fù)延曙?
當(dāng)開(kāi)啟的時(shí)候,所有請(qǐng)求都不會(huì)進(jìn)行轉(zhuǎn)發(fā)亡哄;一段時(shí)間之后(默認(rèn)是5s)枝缔,這個(gè)時(shí)候斷路器是半開(kāi)狀態(tài),會(huì)讓其中一個(gè)請(qǐng)求進(jìn)行轉(zhuǎn)發(fā)蚊惯,如果成功愿卸,斷路器會(huì)關(guān)閉,若失敗截型,繼續(xù)開(kāi)啟趴荸。重復(fù)上述步驟。
以下配置宦焦,即為服務(wù)熔斷
@HystrixCommand(fallbackMethod="paymentCircuitBreakerFallback", commandProperties = {
@HystrixProperty(name="circuitBreaker.enabled", value="true"),//是否開(kāi)啟斷路器
@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(@PathVariable("id") Integer id){
if(id<0) {
throw new RuntimeException("id不能為負(fù)數(shù)");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+" ok, no: "+serialNumber;
}
public String paymentCircuitBreakerFallback(@PathVariable("id") Integer id) {
return "id 不能為負(fù)赊舶,請(qǐng)重試..., id: "+id;
}