文章目錄
-
- 前言:
- 一、目前架構問題分析
-
-
- 1.1 問題分析
- 1.2 雪崩效應
- 1.3 解決方案
-
-
- 1.3.1 逾時機制
- 1.3.2 斷路器模式
-
-
- 二、Hystrix簡介
-
-
- 2.1 Hystrix是什麼:
- 2.2 Hystrix的作用:
- 2.3 Hystrix實作原理:
-
- 三、Hystrix 實戰——為服務消費者添加Hystrix
-
-
- 3.1 pom檔案添加依賴
- 3.2 修改服務消費者(車票微服務)的service方法
-
-
- 3.2.1 添加注解@HystrixCommand
- 3.2.2 編寫處理錯誤的fallback方法
-
- 3.3 修改服務消費者(車票微服務)的啟動類
- 3.4 啟動服務進行測試
-
- 四、Hystrix的Health Indicator及Metrics Stream
-
-
- 4.1 pom檔案添加依賴
- 4.2 修改服務消費者(車票微服務)的application.properties配置檔案
- 4.3 啟動服務進行測試
-
-
- 4.3.1 測試Health Indicator
- 4.3.2 測試Metrics Stream
-
-
- 五、Hystrix Dashboard
-
-
- 5.1 pom檔案添加依賴
- 5.2 修改服務消費者的啟動類
- 5.3 啟動服務進行測試
-
前言:
Spring Cloud系列教程的所有部落格均在下方的目錄連結中,友善大家查找和閱讀。建議按照順序學習,對于項目搭建有疑問的可以着重看目錄裡的第二篇部落格。
Spring cloud學習專欄目錄
一、目前架構問題分析
1.1 問題分析
我們目前的架構如下圖所示,使用Eureka進行服務發現服務注冊,用戶端使用Ribbon進行負載均衡。現在我們假設一種情況,如果服務提供者的響應非常緩慢,那麼服務消費者對服務提供者的請求就會被強制等待,直到http請求逾時,然後抛出異常。那麼在高負載場景下,如果不做任何處理,這種問題很可能造成所有處理使用者請求的線程都被耗竭,而不能響應使用者的進一步請求。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSP9c2YsR3RihGZIpFakNjYv50MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzkzMyQDMwYTMxAjMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
1.2 雪崩效應
在微服務架構中通常會有多個服務層調用,大量的微服務通過網絡進行通信,進而支撐起整個系統。各個微服務之間也難免存在大量的依賴關系。然而任何服務都不是100%可用的,網絡往往也是脆弱的,是以難免有些請求會失敗。基礎服務的故障導緻級聯故障,進而造成了整個系統的不可用,這種現象被稱為服務雪崩效應。服務雪崩效應描述的是一種因服務提供者的不可用導緻服務消費者的不可用,并将不可用逐漸放大的過程。
如下圖所示,A作為服務提供者,B為A的服務消費者,C和D是B的服務消費者。A不可用引起了B的不可用,并将不可用像滾雪球一樣放大到C和D時,雪崩效應就形成了。
1.3 解決方案
1.3.1 逾時機制
通過網絡請求其他服務時,都必須設定逾時時間。正常情況下,一個遠端調用一般在幾十毫秒内就傳回了。當依賴的服務不可用,或者因為網絡問題,響應時間将會變得很長(幾十秒)。而通常情況下,一次遠端調用對應了一個線程/程序,如果響應太慢,那這個線程/程序就會得不到釋放。而線程/程序都對應了系統資源,如果大量的線程/程序得不到釋放,并且越積越多,服務資源就會被耗盡,進而導緻資深服務不可用。是以必須為每個請求設定逾時。
1.3.2 斷路器模式
斷路器模式我們可以把它類比成家中的電閘,如果沒有電閘當電流過載了(如功率過大、短路等),如果電路不斷開,電路就會升溫,造成電路燒壞或是起火等危險。有了電閘之後,當電流過載時,會自動跳閘,進而保護整條電路的安全。當電流過載的問題被解決後,隻要将關閉電閘,電路就又可以正常工作了。
同理在伺服器中,當依賴的服務(服務提供者)出現大量逾時的情況,再讓新的請求去通路已經沒有太大意義,隻會消耗現有的資源。譬如我們設定了逾時時間為1秒,如果短時間内有大量的請求(譬如50個)在1秒内都得不到響應,往往就意味着異常。此時就沒有必要讓更多的請求去通路這個依賴了,我們應該使用斷路器避免資源浪費。
斷路器可以實作快速失敗,如果它在一段時間内偵測到許多類似的錯誤(譬如逾時),就會強迫其以後的多個調用快速失敗,不再請求所依賴的服務,進而防止應用程式不斷地嘗試執行可能會失敗的操作,這樣應用程式可以繼續執行而不用等待修正錯誤,或者浪費CPU時間去等待長時間的逾時。斷路器也可以使應用程式能夠診斷錯誤是否已經修正,如果已經修正,應用程式會再次嘗試調用操作。
斷路器包含三種狀态打開、半開和關閉。
正常情況下處于關閉狀态,如果發生異常斷路器打開。斷路器打開一定時間後,會進入半開狀态去檢測服務是否恢複正常,如果沒有恢複就回來打開狀态,在經過一定的時間在恢複為半開狀态。如果檢測到服務正常了,就變成關閉狀态。
二、Hystrix簡介
2.1 Hystrix是什麼:
Hystrix是一個延遲和容錯庫,可通過添加延遲容限和容錯邏輯來控制分布式服務之間的互動。Hystrix通過隔離服務之間的通路點,停止服務之間的級聯故障并提供後備選項來實作此目的,所有這些都可以提高系統的整體彈性。
2.2 Hystrix的作用:
- 通過第三方用戶端庫提供保護,并控制延遲和失敗依賴項的故障。
- 停止複雜的分布式系統中的級聯故障。
- 快速失敗,迅速恢複。
- 回退并在可能的情況下正常降級。
- 啟用近乎實時的監視,警報和操作控制。
2.3 Hystrix實作原理:
- 包裹請求:将對外部系統(或“依賴項”)的所有調用包裝在通常在單獨線程中執行的HystrixCommand或HystrixObservableCommand對象中(這是指令模式的示例)。
- 資源隔離:為每個依賴項維護一個小的線程池(或信号燈);如果已滿,發往該依賴項的請求将立即被拒絕,而不是排隊也就是快速失敗。
- 實時監控:幾乎實時監控名額和配置更改。測量成功,失敗(用戶端抛出的異常),逾時和線程拒絕。
- 斷路器:如果某個服務的錯誤百分比超過門檻值,則使斷路器跳閘,以在一段時間内手動或自動停止所有對特定服務的請求。
- 回退機制:當請求失敗,被拒絕,逾時或短路時執行回退邏輯。
- 自我修複:斷路器打開一定時間後,會進入半開狀态去檢測服務是否恢複正常,如果沒有恢複就回來打開狀态,在經過一定的時間在恢複為半開狀态。如果檢測到服務正常了,就變成關閉狀态。
三、Hystrix 實戰——為服務消費者添加Hystrix
3.1 pom檔案添加依賴
找到服務消費者的父工程的pom檔案,添加hystrix的依賴。(對于項目搭建有疑問的讀者,請看我spring cloud專欄裡的先前部落格)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<groupId>com.springcloud</groupId>
<artifactId>microservice-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>microservice-provide-user</module>
<module>microservice-consumer-ticket</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- 導入Spring Cloud的依賴管理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- web支援 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot 整合eureka用戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--整合hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 資源檔案拷貝插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- springboot插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.2 修改服務消費者(車票微服務)的service方法
3.2.1 添加注解@HystrixCommand
在需要進行容錯處理的方法上添加注解
3.2.2 編寫處理錯誤的fallback方法
注意方法名要和@HystrixCommand注解中定義的方法名保持一緻,而且傳回值和傳參也要和原方法保持一緻。
package com.spring.ticketservice.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.spring.ticketservice.pojo.User;
@Service
public class TicketService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
/*
* 查詢車票資訊時查詢使用者的資訊(這裡為了簡化操作就不對車票資訊進行查詢,直接去調用查詢使用者資訊的接口)
*/
@HystrixCommand(fallbackMethod = "queryTicketInfoFallbackMethod")
public User queryTicketInfo(Long id) {
String serviceId = "microservice-provide-user";
ServiceInstance serviceInstance = this.loadBalancerClient.choose(serviceId);
//列印出請求了哪個端口的使用者微服務
System.out.println("請求了" + serviceInstance.getPort());
//向使用者微服務中的接口發送請求的位址
//String url = "http://localhost:8081/getUserInfo/"+ id;
String url = "http://microservice-provide-user/getUserInfo/"+ id;
return restTemplate.getForObject(url, User.class);
}
/*
* 處理請求失敗的方法
*/
public User queryTicketInfoFallbackMethod(Long id) {
User user = new User(0l,"出錯了");
return user;
}
}
3.3 修改服務消費者(車票微服務)的啟動類
添加注解@EnableCircuitBreaker
package com.spring.ticketservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import com.spring.config.TestConfiguration;
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "microservice-provide-user",configuration = TestConfiguration.class)
@EnableCircuitBreaker
public class TicketServiceApplication {
public static void main(String[] args) {
SpringApplication.run(TicketServiceApplication.class, args);
}
/*
* 向Spring容器中添加RestTemplate對象
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3.4 啟動服務進行測試
我們啟動Eureka服務、使用者微服務、車票微服務。
通路eureka可以看到兩個服務啟動成功,開始測試
———————————————————————————————————
通路車票微服務的接口 http://localhost:8082/getTicketInfo/1,可以看到現在是正常的情況。
———————————————————————————————————
關閉使用者微服務,再次通路該接口可以看到執行力我們自定義的fallback方法,傳回了一個name為出錯了的user對象。說明hystrix發揮了作用,
四、Hystrix的Health Indicator及Metrics Stream
4.1 pom檔案添加依賴
找到服務消費者的父工程的pom檔案,添加健康監控的依賴
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<groupId>com.springcloud</groupId>
<artifactId>microservice-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>microservice-provide-user</module>
<module>microservice-consumer-ticket</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- 導入Spring Cloud的依賴管理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- web支援 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot 整合eureka用戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--整合hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- 健康監控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 資源檔案拷貝插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- springboot插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.2 修改服務消費者(車票微服務)的application.properties配置檔案
預設情況下actuator/health節點應該是開放的,但是在我測試的過程中發現通路會404報錯,于是我就加上了management.endpoints.web.exposure.include=*來開放所有的節點。
如果你隻想看到hystrix 斷路器的狀态那麼就不用配置檢視詳細的健康資訊management.endpoint.health.show-details=always。
# 配置api端口号
server.port=8082
# tomcat
server.tomcat.uri-encoding=UTF-8
# 服務名稱,也就是在eureka
spring.application.name=microservice-consumer-ticket
# 是否啟動注冊,這是一個用戶端需要注冊
eureka.client.register-with-eureka=true
# 是否檢索服務
eureka.client.fetch-registry=true
# 服務注冊中心的位址
eureka.client.service-url.default-zone=http://localhost:8761/eureka
#eureka.client.service-url.default-zone=http://admin:123456@localhost:8761/eureka
#開放所有的節點
management.endpoints.web.exposure.include=*
#顯示詳細的健康資訊
management.endpoint.health.show-details=always
4.3 啟動服務進行測試
啟動eureka微服務,車票微服務。
4.3.1 測試Health Indicator
通路車票微服務的端口後加上actuator/health,可以看到列印出了詳細的健康資訊,包括服務本身的運作情況、斷路器是否打開等等。
4.3.2 測試Metrics Stream
接下來我們通路 actuator/hystrix.stream來看一下結果,可以看到左邊一直出現ping:。Metrics Stream其實就是一個對于api接口的監控,監控每一個接口的狀态,可以看到他是一直在重新整理的。
———————————————————————————————————
我們再來做進一步的測試,通路車票微服務中的接口,然後我們在回到剛才的頁面,可以看到出現了很多資訊,說明他在進行監控。
五、Hystrix Dashboard
通過actuator/hystrix.stream可以實時看到最新的監控資料,但是看起來很不友善。所有spring cloud提供了一個Hystrix Dashboard,可以以圖形化界面展示這些資料友善我們的閱讀。
5.1 pom檔案添加依賴
找到服務消費者(車票微服務)的父工程的pom檔案,添加dashboard的依賴。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<groupId>com.springcloud</groupId>
<artifactId>microservice-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>microservice-provide-user</module>
<module>microservice-consumer-ticket</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- 導入Spring Cloud的依賴管理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- web支援 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot 整合eureka用戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--整合hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- 健康監控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- hystrix 儀表盤 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 資源檔案拷貝插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- springboot插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5.2 修改服務消費者的啟動類
添加@EnableHystrixDashboard注解
package com.spring.ticketservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import com.spring.config.TestConfiguration;
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "microservice-provide-user",configuration = TestConfiguration.class)
@EnableCircuitBreaker
@EnableHystrixDashboard
public class TicketServiceApplication {
public static void main(String[] args) {
SpringApplication.run(TicketServiceApplication.class, args);
}
/*
* 向Spring容器中添加RestTemplate對象
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
5.3 啟動服務進行測試
啟動eureka服務、車票微服務、使用者微服務。
浏覽器通路車票微服務的位址後加/hystrix http://localhost:8082/hystrix可以看到來到了Hystrix Dashboard的首頁。
———————————————————————————————————
輸入位址 http://localhost:8082/actuator/hystrix.stream 以及名稱後點選Monitor Stream
———————————————————————————————————
進入到如下頁面,可以看到這裡有接口queryTicketInfo的相關資訊。
———————————————————————————————————
通路車票微服務中的接口數次(這裡6次)可以看到頁面裡的數值發生了變化, 說明他在實時的監控。
下圖為各項數值所代表的含義