一、前言
在微服務應用中,服務存在一定的依賴關係,如果某個目標服務調用慢或者有大量超時造成服務不可用,間接導致其他的依賴服務不可用,最嚴重的可能會阻塞整條依賴鏈,最終導致業務系統崩潰(又稱雪崩效應)。
上述的問題將是本篇需要解決的問題。
二、簡單介紹
# 2.1 請求熔斷
斷路器是一種開關設置,當某個服務單元發生故障之後,通過斷路器的故障監控,向調用方返回一個符合預期的服務降級處理(fallback),而不是長時間的等待或者拋出調用方無法處理的異常,這樣保證了服務調用方的線程不會長時間被佔用,從而避免了故障在分佈式系統的蔓延乃至崩潰。
# 2.2 服務降級
fallback 相當於是降級操作。對於查詢操作, 我們可以實現一個 fallback 方法, 當請求後端服務出現異常的時候, 可以使用 fallback 方法返回的值。 fallback 方法的返回值一般是設置的默認值或者來自緩存,告知後面的請求服務不可用了,不要再請求了。
# 2.3 請求熔斷和服務降級區別
相同:
目標一致:爲了防止系統崩潰而實施的一種防禦手段
表現形式一致:當請求目標在一定時間內無響應時,返回或執行默認響應內容
不同:
觸發條件不同:下游服務出現故障觸發請求熔斷。系統負荷超過閾值觸發服務降級。
管理目標層次不同:請求熔斷針對所有微服務。服務降級針對整個系統中的外圍服務。
# 2.4 實現方案
Spring Cloud Hystrix 實現了斷路器、線程隔離等一系列服務保護功能。它是基於 Netflix 的開源框架 Hystrix 實現的,該框架的目的在於通過控制訪問遠程系統、服務和第三方庫節點,從而對延遲和故障提供更強大的容錯能力。
Hystrix 具備服務熔斷、服務降級、線程和信號隔離、請求緩存、請求合併以及服務監控的能力。
三、請求熔斷實戰
本次測試案例基於之前發表的文章中介紹的案例進行演示,不清楚的讀者請先轉移至 《Spring Cloud 入門 之 Feign 篇(三)》 進行瀏覽。
現在的項目列表如下:
服務實例 | 端口 | 描述 |
---|---|---|
common-api | - | 公用的 api,如:實體類 |
eureka-server | 9000 | 註冊中心(Eureka 服務端) |
goods-server | 8081 | 商品服務(Eureka 客戶端) |
goods-server-02 | 8082 | 商品服務(Eureka 客戶端) |
goods-server-03 | 8083 | 商品服務(Eureka 客戶端) |
order-server | 8100 | 訂單服務(Eureka 客戶端) |
在 order-server 項目中:
# 3.1 添加依賴
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
# 3.2 設置熔斷策略
我們來修改獲取下訂單的方法,在 placeOrder 方法上加 @HystrixCommand 註解:
@Service
public class OrderServiceImpl implements OrderService{
@Autowired
private RestTemplate restTemplate;
// @Autowired
// private GoodsServiceClient goodsServiceClient;
@HystrixCommand(fallbackMethod = "defaultByPlaceOrder")
@Override
public void placeOrder(Order order) throws Exception{
Result result = this.restTemplate.getForObject("http://GOODS/goods/goodsInfo/" + order.getGoodsId(), Result.class);
// Result result = this.goodsServiceClient.goodsInfo(order.getGoodsId());
if (result != null && result.getCode() == 200) {
System.out.println("=====下訂單====");
System.out.println(result.getData());
} else {
System.out.println(result.getMsg());
}
}
public void defaultByPlaceOrder(Order order) {
System.out.println("商品服務系統異常");
}
}
當調用商品服務超時或出現異常時,Hystrix 會調用 @HystrixCommand 中指定的 fallbackMethod 方法獲取返回值或執行異常處理。
注意:fallbackMethod 方法要求與正常方法有相同的入參和回參。
# 3.3 啓動熔斷功能
在啓動類上添加 @EnableCircuitBreaker 註解:
@EnableCircuitBreaker
@EnableEurekaClient
@SpringBootApplication
public class OrderServerApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServerApplication.class, args);
}
}
# 3.4 熔斷測試
- 我們首先演示沒有開啓熔斷的功能,即先把上邊的 @EnableCircuitBreaker 註解進行註釋。
啓動好所有項目,使用 Postman 請求 order-server 進行下單操作,運行結果如下:
當我們請求發送的 goodsId 的商品不存在,服務提供方拋會異常,調用方無法處理,因此只能展示圖中的異常信息。
2.下面,我們再將 @EnableCircuitBreaker 註解的註釋放開,運行結果如下:
從圖中可知,雖然請求了一個 goodsId 不存在的商品,但是調用方(order-server)開啓了熔斷機制,執行默認方法,從而使接口能正常通信而不是拋出調用方不可處理的異常導致整個系統不能正常運行。
看到這裏,或許會有讀者產生一個疑問,如果類中定義 N 個方法,是不是意味着同時也要定義 N 個異常處理的方法呢,答案是否定的。
Hystrix 還提供了 @DefaultProperties 統一處理請求熔斷,在該註解上設置 defaultFallback 屬性值,即熔斷開啓後要執行的方法。
@Service
@DefaultProperties(defaultFallback = "defaultByHystrix")
public class OrderServiceImpl implements OrderService{
// @Autowired
// private RestTemplate restTemplate;
@Autowired
private GoodsServiceClient goodsServiceClient;
@HystrixCommand
@Override
public void placeOrder(Order order) throws Exception{
// Result result = this.restTemplate.getForObject("http://GOODS/goods/goodsInfo/" + order.getGoodsId(), Result.class);
Result result = this.goodsServiceClient.goodsInfo(order.getGoodsId());
if (result != null && result.getCode() == 200) {
System.out.println("=====下訂單====");
System.out.println(result.getData());
} else {
System.out.println(result.getMsg());
}
}
public void defaultByHystrix() {
System.out.println("商品服務系統異常");
}
}
注意:defaultFallback 定義的方法必須是無參的。
四、服務降級實戰
在 common-api 項目中:
# 4.1 定義 Fallback
@Component
public class GoodsServiceClientFallbackFactory implements FallbackFactory<GoodsServiceClient> {
@Override
public GoodsServiceClient create(Throwable cause) {
return new GoodsServiceClient() {
@Override
public Result goodsInfo(String goodsId) {
return Result.fail(500, "商品服務系統出現異常,請聯繫管理員");
}
};
}
}
使用單獨的類處理異常邏輯,當與服務端無法正常通信時調用此類中的方法返回結果。
# 4.2 修改 Feign 客戶端
將上邊定義好的 FallbackFactory 設置到 @FeignClient 註解上:
@FeignClient(value="GOODS", fallbackFactory = GoodsServiceClientFallbackFactory.class)
public interface GoodsServiceClient {
@RequestMapping("/goods/goodsInfo/{goodsId}")
public Result goodsInfo(@PathVariable("goodsId") String goodsId);
}
# 4.3 開啓服務降級功能
在 order-server 項目中:
server:
port: 8100
spring:
application:
name: ORDER
eureka:
instance:
instance-id: order-api-8100
prefer-ip-address: true # 訪問路徑可以顯示 IP
client:
service-url:
defaultZone: http://localhost:9000/eureka/ # 註冊中心訪問地址
feign:
hystrix:
enabled: true
# 4.4 去掉 @HystrixCommand 配置
@Service
//@DefaultProperties(defaultFallback = "defaultByHystrix")
public class OrderServiceImpl implements OrderService{
// @Autowired
// private RestTemplate restTemplate;
@Autowired
private GoodsServiceClient goodsServiceClient;
// @HystrixCommand
@Override
public void placeOrder(Order order) throws Exception{
// Result result = this.restTemplate.getForObject("http://GOODS/goods/goodsInfo/" + order.getGoodsId(), Result.class);
Result result = this.goodsServiceClient.goodsInfo(order.getGoodsId());
if (result != null && result.getCode() == 200) {
System.out.println("=====下訂單====");
System.out.println(result.getData());
} else {
System.out.println(result.getMsg());
}
}
// public void defaultByHystrix() {
// System.out.println("商品服務系統異常");
// }
}
# 4.5 測試服務降級
在啓動類上加 FallbackFactory 類的包掃描目錄:
@ComponentScan(basePackages = {"com.extlight.springcloud"}) // 爲了能掃描 common-api 項目中的 GoodsServiceClientFallbackFactory
@EnableFeignClients(basePackages = {"com.extlight.springcloud"})
@EnableEurekaClient
@SpringBootApplication
public class OrderServerApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServerApplication.class, args);
}
}
打開 Postman 請求下單接口,結果如下圖:
我們手動關閉 2 個商品服務,保留一個商品服務並多次請求商品服務接口,從而出模擬商品服務超過預定荷載情景,最終看到圖中服務降級功能。當有請求再次訪問商品服務時默認返回 GoodsServiceClientFallbackFactory 中定義的內容。
五、儀表盤
除了服務熔斷、降級的功能外,Hystrix 還提供了準及時的調用監控。 Hystrix 會持續地記錄所有通過 Hystrix 發起的請求的執行信息,並以統計報表和圖形方式展示給用戶。
# 5.1 配置被監控方
order-server 項目中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
修改 application.yml,開放端口:
management:
endpoints:
web:
exposure:
include: "*"
# 5.2 配置監控方
1.新建一個名爲 hystrix-dashboard 項目,添加如下依賴:
<!-- hystrix-dashboard -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
2.新建 application.yml
server:
port: 9300
spring:
application:
name: Hystrix-Dashboard
3.開啓監控功能
在啓動類上添加 @EnableHystrixDashboard 註解。
@EnableHystrixDashboard
@SpringBootApplication
public class HystrixdashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixdashboardApplication.class, args);
}
}
啓動,瀏覽器訪問: http://localhost:9300/hystrix:
# 5.3 監控設置
我們以監控 order-server 爲例,在監控界面添加監控信息:
# 需要監控的服務地址
http://localhost:8100/actuator/hystrix.stream
delay: 請求間隔時間
title: 監控名稱
點擊 monitor stream
批量訪問 order-server 服務的下單接口。
最終效果如下:
通過批量訪問下單接口,發現圖中實心圓和曲線發生了變化。那我們如何根據這兩個圖形查看監控信息呢?
實心圓:通過顏色的變化代表實例的健康程度,健康度從綠色>黃色>橙色>紅色遞減。其大小也會根據實例的請求流量發生變化,流量越大實心圓越大。
曲線:用來記錄間隔時間內流量的相對變化,通常可以觀察到流量的上升和下降趨勢。
六、案例源碼
七、參考資料
- 本文作者: MoonlightL
- 本文鏈接: https://www.extlight.com/2019/03/19/Spring-Cloud-入門-之-Hystrix-篇(四)/
- 版權聲明: 本博客所有文章除特別聲明外均爲原創,採用 CC BY-NC-SA 4.0 許可協議。轉載請在文章開頭明顯位置註明原文鏈接和作者等相關信息,明確指出修改(如有),並通過 E-mail 等方式告知,謝謝合作!