什麼是服務雪崩?
單個服務發生故障,占用過多的系統資源,進而導緻級聯故障的情況稱為服務雪崩。
什麼是Hystrix?
在分布式環境中,許多服務依賴項中的一些必然會失敗。(服務挂了)
Hystrix是一個庫,通過添加延遲容忍和容錯邏輯,控制這些分布式服務之間的互動。
Hystrix通過隔離服務之間的通路點、停止級聯失敗和提供回退選項來實作這一點,所有這些都可以提高系統的整體彈性。
容錯:允許犯錯,在微服務開放中主要展現在服務故障。
簡言之,Hystrix是一個實作容錯機制的元件。【也是實作高可用的目的】
Hystrix的主要作用
- 為網絡請求設定逾時
- 使用斷路器模式
什麼是斷路器模式?
家用空開就是一種斷路器模式,前身是保險絲。假設某個電器負載過大而損壞,空開會跳閘,而保險絲會熔斷。
假設沒有空開或者保險絲呢?引起更大的電路故障,甚至導緻火災,再擴張可能會燒到鄰居家的房子。
對于微服務來說同樣如此,當某一個服務出現問題時,使用斷路器關停服務,不會導緻由于持續通路導緻的資源占有進而引起其他服務的正常運作。
服務熔斷與服務降級
服務熔斷指的是當網絡請求達到某一個門檻值(可設定)時,為了防止服務過載,占用系統資源,暫停該服務的調用,使服務降級。【服務沒挂,但是擔心挂了,就讓服務暫時休息一下】
服務降級涉及的範圍更大,
- 逾時降級:主要配置好逾時時間和逾時重試次數和機制,并使用異步機制探測回複情況
- 失敗次數降級:主要是一些不穩定的api,當失敗調用次數達到一定閥值自動降級,同樣要使用異步機制探測回複情況
- 故障降級:比如要調用的遠端服務挂掉了(網絡故障、DNS故障、http服務傳回錯誤的狀态碼、rpc服務抛出異常),則可以直接降級。降級後的處理方案有:預設值(比如庫存服務挂了,傳回預設現貨)、兜底資料(比如廣告挂了,傳回提前準備好的一些靜态頁面)、緩存(之前暫存的一些緩存資料)
-
限流降級
當我們去秒殺或者搶購一些限購商品時,此時可能會因為通路量太大而導緻系統崩潰,此時開發者會使用限流來進行限制通路量,當達到限流閥值,後續請求會被降級;降級後的處理方案可以是:排隊頁面(将使用者導流到排隊頁面等一會重試)、無貨(直接告知使用者沒貨了)、錯誤頁(如活動太火爆了,稍後重試)。
如何了解服務熔斷和服務降級的差異?
服務熔斷的場景是請求次數過多而設計的一種保護政策。而服務降級是着眼于整個系統的各種問題(逾時,故障等等)。服務熔斷會引起服務降級。換句話說,熔斷是降級的一部分。
@HystrixCommand注解
預設開啟線程池隔離,服務熔斷,服務降級
接着上次的工程做些修改:【和feign結合使用】
https://github.com/HCJ-shadow/Feign
- 建立工程msc-consumer-hystrix-80

- pom依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- Eureka用戶端啟動需要依賴web子產品-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-openfeign</artifactId>-->
<!-- </dependency>-->
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
複制
-
controller
package zkrun.top.controller; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import zkrun.top.service.Hystrix_FeignService; @RestController public class Hystrix_FeignController { @Autowired Hystrix_FeignService hystrix_feignService; @RequestMapping(value = "/feign/info/get",method = RequestMethod.GET) @HystrixCommand(fallbackMethod = "hystrix_fallback") public String request() { return this.hystrix_feignService.request(); } public String hystrix_fallback() { return "目前服務故障,服務熔斷已啟動!"; } }
- application.yaml
server:
port: 80
eureka:
client:
service-url:
defaultZone: http://eureka6001.com:6001/eureka/,http://eureka6002.com:6002/eureka/,http://eureka6003.com:6003/eureka/
spring:
application:
name: hystrix-consumer
複制
- 主啟動類【@EnableCircuitBreaker】
package zkrun.top;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
@EnableCircuitBreaker
public class App_Consumer_Hystrix_Feign_80
{
public static void main(String[] args)
{
SpringApplication.run(App_Consumer_Hystrix_Feign_80.class, args);
}
}
複制
運作測試:
- 啟動三個Eureka,
- 啟動5001,5002,5003
- 啟動hystrix80
測試服務熔斷
使用Jmeter
http://jmeter.apache.org/download_jmeter.cgi
解壓後輕按兩下bin下面的jmeter.bat即可
設定線程數為100
設定相關資訊
執行測試,檢視結果樹
額嗯,完全抗的住啊!!!
設定線程數為500:
出現熔斷情況。
測試服務降級
設定故障降級,把5001關停
調用降級方法,重複幾次之後,将不再通路5001。【Ribbon的RetryRule政策】
假設重新開機服務,
即可正常通路。
小結:
- 為了保持高可用,應用可以配置多份,這樣即使故障一台,對外仍舊可以保持可用性。但是随之而來的是資料庫的一緻性問題。CAP理論的A與C隻能選擇一個也是這個原理。根據實際的業務情況,哪些業務是必須保持高可用的,而哪些是必須保持一緻性的,需要進一步分析和判斷。
- Hystrix實作服務的熔斷和降級政策的自由度很高,了解其原理,搭配Feign中內建的RIbbon通路算法,可以實作更高的擴充群組合。
代碼參考:https://github.com/HCJ-shadow/Hystrix