什么是服務雪崩?
單個服務發生故障,占用過多的系統資源,從而導致級聯故障的情況稱為服務雪崩。
什么是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/Noneplus/JavaDev-Note/tree/master/SpringCloud代碼