在基於Spring Cloud的微服務架構體系下,按照系統功能邊界的不一樣劃分,原先大而全的系統會被拆分爲多個不一樣的微服務,而相應的微服務會提供一組功能關聯的服務接口,並向系統中的其餘微服務提供服務。在正常狀況下,各個微服務之間功能上相互解耦,從軟件的設計上來說會呈現出一個比較合理的狀態,可是從調用鏈路上來看,這種拆分實際上也是拉長了外部服務請求的調用鏈路。spring
舉個例子,在創業公司的早期,考慮到研發維護成本,系統架構設計很簡單,從軟件結構上看,就是一個api服務面向app端,一個service端面向後臺功能。以用戶購物場景舉例,雖然這個過程邏輯上會經歷商品、下單、支付、物流、庫存等複雜邏輯的處理,可是由於這些邏輯都耦合在一個系統中,因此從用戶的app到後臺服務,服務的調用鏈路並不算太長。即使在這樣的狀況下,仍是會存在若是請求量忽然劇增,服務端的業務處理線程池被塞滿,整個後臺系統的數據庫鏈接資源、緩存資源所有被耗盡,從而致使整個服務不可用的狀況。數據庫
而隨着公司的逐步發展,業務請求量與日劇增,爲了提升整個系統的吞吐量及可用性,咱們採用了微服務架構的設計,將原先的系統拆分紅了商品、訂單、支付、物流、庫存等多個微服務,而這些服務之間經過網絡進行通訊(以Spring Cloud來講就是經過咱們前面說到的FeignClient進行服務發現後,以HTTP的方式進行網絡調用),造成了一次購物請求,會經歷app端調用商品微服務進行瀏覽,選中商品後由商品中心調用訂單中心進行下單,而後訂單中心調用支付系統進行付款,付款成功後,訂單中心再調用物流中心進行發貨,與此同時,物流中心調用庫存系統進行庫存減小的漫長調用鏈路。bootstrap
這個流程看起來就有點長了,爲了方便你們理解,仍是來張圖:api
如上圖所示,在系統微服務化後,雖然此時每一個微服務都擁有獨立的進程資源、業務線程池以及單獨的數據庫,總體的系統吞吐量比之前高了不少,而且每一個微服務也都是集羣部署。可是由於整個調用的網絡鏈路是很是長的,若是此時發生局部網絡或者部分微服服務故障的話,依然可能會致使整個微服務系統的癱瘓。緩存
舉個例子,假設此時物流服務發生了宕機,可是前面的微服務都不知道,由於整個鏈路調用都是同步的,因此此時訂單服務調用物流微服務的時候會出現部分線程阻塞直至超時異常,同理調用物流的微服務的訂單服務的那個線程也會出現阻塞,假如此時業務請求併發量很是高,由於線程阻塞時間過長,那麼很快訂單微服務及物流微服務的業務線程數資源就會被耗盡,此時用戶App端就會出現不只購物功能沒法使用,就連商品瀏覽也不行了,而此時請求量繼續增長,狀況就會更加惡化,若是業務線程池使用的是無界隊列(線程池請求隊列),最終還會致使系統內存溢出,此時系統要想自動恢復可能就比較困難了,糟糕的狀況就是服務持續不可用,而最終可能只能採用重啓整個系統的高昂成原本臨時解決下,而這也還不能最終解決問題,由於重啓後狀況依然可能會發生(若是並無排查及解決掉物流微服務故障緣由的話)。bash
從上面的例子看,一個微服務的故障竟然能致使整個系統的崩潰,而咱們但願的狀況是若是發現物流微服務持續故障的話,此時訂單微服務應該是能夠感知到,並根據必定的機制進行容錯,即訂單微服務在知道物流微服務異常的狀況下,就暫時先不要把請求發送到物流微服務了,給物流微服務先限流,而在自身本地邏輯中採起一個默認容錯邏輯進行熔斷後馬上返回App調用端,例如,能夠先將須要發送的消息緩存,待物流微服務恢復後再從新發送。這樣的話,故障的物流微服務也就不會致使訂單服務由於同步調用鏈路超時過長而出現級聯故障了。網絡
那麼在Spring Cloud微服務設計中如何才能實現這樣的機制呢?這裏涉及到幾個問題:架構
以上這些問題,就是本章要講述的如何在Spring Cloud微服務設計中實現服務熔斷限流的內容了!而這一點對於併發量很是高的狀況下,實現微服務的可用性是很重要的一個方面。併發
在Spring Cloud微服務設計中須要經過集成Hystrix框架來實現微服務間的熔斷保護機制,Hystrix框架會經過監控微服務之間的調用狀況,來決定是否啓動熔斷保護。那麼接下來,就讓咱們一塊兒來看下如何在Spring Cloud項目中經過集成Hystrix框架來實現熔斷機制吧!app
引入依賴 要Spring Cloud中使用Hystrix框架,須要引入Hystrix框架的starter依賴包,以下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
複製代碼
經過引入此stater依賴包,咱們就能夠基於Spring Boot框架的特性,實現對Hystrix框架的開箱即用了。
註解開啓熔斷器 在Spring Cloud微服務中啓用熔斷器,須要在微服務的Application主程序上添加org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker註解,如:
@EnableDiscoveryClient
@EnableCircuitBreaker
@SpringBootApplication
@EnableFeignClients(basePackageClasses = {PaymentClient.class})
@EnableScheduling
public class Goods {
public static void main(String[] args) {
SpringApplication.run(Goods.class, args);
}
}
複製代碼
經過這樣一個簡單的註解配置,此時微服務就開啓了基於Hystrix的斷路器功能。須要說明的是,在某個微服務中開啓斷路器,實現的是該微服務對其下游微服務的熔斷功能,而不是該微服務對其上游調用的熔斷,這一點你們不要混淆了,由於在Spring Cloud的微服務體系下,熔斷的實現是基於Hystrix本地庫來實現的,本質上是客戶端熔斷,而不是服務端的熔斷。相比較於最近談論比較多的基於Service Mesh的限流熔斷功能而言,基於客戶端的熔斷從應用的形態上看,是與微服務自己融合在一塊兒的,而不是獨立的服務。
FeignClient開啓Hystrix 在微服務中開啓斷路器後,並不表示就能夠馬上使用了,在前面的章節中咱們講過,在Spring Cloud微服務體系中,微服務之間的通訊交互須要經過使用FeignClient來進行,而默認狀況下FeignClient中默認狀況下是禁用Hystrix的,因此若是須要在微服務中啓用Hystrix的熔斷功能,則須要經過配置手動開啓Hystrix功能,這樣FeignClient客戶端在微服務之間進行通信調用時,才能在感知到微服務異常的狀況下,將錯誤指標信息反饋給Hystrix框架,從而Hystrix才能根據自身邏輯對熔斷器的狀態進行啓停(關於Hystrix的具體運行原理,咱們在後面的章節中進行介紹)。
如下是咱們在項目的bootstrap.yml文件中,開啓FeignClient對Hystrix支持的配置:
feign:
hystrix:
enabled: true
複製代碼
Spring Cloud中微服務之間的服務調用是基於FeignClient的,在實際的工程實踐中,咱們通常會單獨將微服務的FeignClient調用端代碼進行抽離,並以SDK jar包依賴的形式進行發佈。通常狀況下,能夠每一個微服務都抽離一個FeignClient工程代碼,這樣更加清晰;若是以爲太過於麻煩,也能夠把多個不一樣微服務的FeignClient客戶端代碼耦合在一塊兒,全部的微服務依賴這一個SDK也能夠,只是後期若是微服務的數量比較多,而且維護團隊比較分散的話,這樣也會致使一個很臃腫的項目出現,維護升級更加麻煩而已,你們能夠根據團隊的實際狀況進行規劃。
咱們在前面講述過基於Spring Cloud的微服務的熔斷機制,其實是基於Hystrix框架的客戶端熔斷機制,也就是說上游微服務在經過FeignClient調用下游微服務的時候,若是感知到下游微服務調用異常須要向上向Hystrix框架反饋異常,若是Hystrix框架計算異常指標達到了閥值就會開啓熔斷器。而以後FeignClient客戶端針對該下游微服務的調用,就須要被Hystrix熔斷後回調一個相應的本地降級處理方法,從而實現服務降級。
而FeignClient從代碼的角度已經支持了這樣的設計,咱們在經過@FeignClient註解編寫微服務的客戶端調用代碼時,就能夠經過指定相應的Fallback類來處理服務被熔斷後的降級邏輯。下面咱們就以本文舉例的項目示例,來編寫訂單微服務的FeignClient客戶端SDK代碼:order-client。
代碼示例:
@FeignClient(value = "order", configuration = OrderClientConfiguration.class, fallback = OrderClientFallback.class)
public interface OrderClient {
// 查詢購物訂單扣費狀態(內)
@RequestMapping(value = "/order/queryOrderCost", method = RequestMethod.GET)
QeuryOrderCostResVo queryOrderCost(@RequestParam(value = "orderId") String orderId) throws InternalApiException;
}
複製代碼
根據訂單微服務中的服務接口定義,咱們經過@FeignClient註解定義了一個OrderClient.class類,該類聲明瞭微服務的接口定義,假設這裏訂單微服務提供了一個訂單查詢接口(一個微服務通常狀況下會有多個服務接口,這裏舉一個接口只是爲了好舉例)。
咱們能夠看到在@FeignClient註解的屬性中,有一個fallback屬性,這個屬性指定了一個服務降級的配置類OrderClientFallback.class。這樣,就能夠在該類中實現微服務對應方法的降級邏輯了:
public class OrderClientFallback implements OrderClient {
@Override
public OrderCostDetailVo orderCost(String orderId, long userId, String busiId, String orderType, int duration,
int bikeType, String bikeNo, String countryName, int cityId, int orderCost, String currency, int strategyId,
String tradeTime) {
return new OrderCostDetailVo();
}
}
複製代碼
能夠看到降級處理類其實是OrderClient的一個實現類,因此在這裏每一個微服務的接口都會被強制要求實現相應的熔斷降級代碼。而具體的降級邏輯,則能夠根據服務的具體狀況進行編寫,如這裏是返回一個空的消息對象。
以上模式就是在Spring Cloud中經過FeignClient調用時,在開啓Hystrix熔斷功能後的基本處理套路了。接下來,咱們經過具體的測試效果,來看下熔斷器功能的生效狀況:
一、在微服務goods中引入order微服務的FeignClient客戶端SDK
<dependency>
<groupId>com.wudimanong.client</groupId>
<artifactId>order-client</artifactId>
<version>1.0.0</version>
</dependency>
複製代碼
二、爲了觀測,咱們須要開啓HystrixDashboard
引入HystrixDashboard依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
複製代碼
在應用程序主類中開啓HystrixDashboard註解:
@EnableHystrixDashboard
@EnableDiscoveryClient
@EnableCircuitBreaker
@SpringBootApplication
@EnableFeignClients
public class GoodsApplication {
public static void main(String[] args) {
SpringApplication.run(GoodsApplication.class, args);
}
}
複製代碼
三、此時經過訪問HystrixDashboard控制檯就能夠看到監控指標信息了
咱們假設goods調用order服務正常狀況下Ciruit是close狀態的,若是此時斷掉order服務,而後多刷幾回goods調用請求,此時,咱們就發現關於order服務的熔斷開關被打開了。
而後咱們恢復order服務,而後再多刷幾回調用接口,就會發現Ciruit就會被關閉了。
經過上面的配置,咱們就基本完成了Spring Cloud項目中關於Hystrix熔斷器的配置了。