天天看點

Spring Cloud 入門 之 Hystrix 篇(四)

原文位址:Spring Cloud 入門 之 Hystrix 篇(四)

部落格位址:http://www.extlight.com

一、前言

在微服務應用中,服務存在一定的依賴關系,如果某個目标服務調用慢或者有大量逾時造成服務不可用,間接導緻其他的依賴服務不可用,最嚴重的可能會阻塞整條依賴鍊,最終導緻業務系統崩潰(又稱雪崩效應)。

上述的問題将是本篇需要解決的問題。

二、簡單介紹

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
goods-server-03 8083
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 熔斷測試

  1. 我們首先示範沒有開啟熔斷的功能,即先把上邊的 @EnableCircuitBreaker 注解進行注釋。

啟動好所有項目,使用 Postman 請求 order-server 進行下單操作,運作結果如下:

Spring Cloud 入門 之 Hystrix 篇(四)

當我們請求發送的 goodsId 的商品不存在,服務提供方抛會異常,調用方無法處理,是以隻能展示圖中的異常資訊。

  1. 下面,我們再将 @EnableCircuitBreaker 注解的注釋放開,運作結果如下:
Spring Cloud 入門 之 Hystrix 篇(四)

從圖中可知,雖然請求了一個 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 開啟服務降級功能

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 請求下單接口,結果如下圖:

Spring Cloud 入門 之 Hystrix 篇(四)

我們手動關閉 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:

Spring Cloud 入門 之 Hystrix 篇(四)

5.3 監控設定

我們以監控 order-server 為例,在監控界面添加監控資訊:

# 需要監控的服務位址
http://localhost:8100/actuator/hystrix.stream

delay: 請求間隔時間

title: 監控名稱

點選 monitor stream

批量通路 order-server 服務的下單接口。
           

最終效果如下:

Spring Cloud 入門 之 Hystrix 篇(四)

通過批量通路下單接口,發現圖中實心圓和曲線發生了變化。那我們如何根據這兩個圖形檢視監控資訊呢?

實心圓:通過顔色的變化代表執行個體的健康程度,健康度從綠色>黃色>橙色>紅色遞減。其大小也會根據執行個體的請求流量發生變化,流量越大實心圓越大。

曲線:用來記錄間隔時間内流量的相對變化,通常可以觀察到流量的上升和下降趨勢。

六、案例源碼

Hystrix demo 源碼

七、參考資料

hystrix-javanica

hystrix configuration

hystrix dashboard

繼續閱讀