在真正學習Hystrix之前,讓我們愉悅的談談Hystrix産生的背景
一、背景介紹
1、服務雪崩
分布式系統環境下,服務間類似依賴非常常見,一個業務調用通常依賴多個基礎服務。如下圖,
如果各個服務正常運作,那大家齊樂融融,高高興興的,但是如果其中一個服務崩壞掉會出現什麼樣的情況呢?如下圖,
當Service A的流量波動很大,流量經常會突然性增加!那麼在這種情況下,就算Service A能扛得住請求,Service B和Service C未必能扛得住這突發的請求。
此時,如果Service C因為抗不住請求,變得不可用。那麼Service B的請求也會阻塞,慢慢耗盡Service B的線程資源,Service B就會變得不可用。緊接着,Service A也會不可用。
So,簡單地講。一個服務失敗,導緻整條鍊路的服務都失敗的情形,我們稱之為服務雪崩。
2、引起雪崩的原因和服務雪崩的三個階段
原因大緻有四:
1、硬體故障;
2、程式Bug;
3、緩存擊穿(使用者大量通路緩存中沒有的鍵值,導緻大量請求查詢資料庫,使資料庫壓力過大);
4、使用者大量請求;
服務雪崩的第一階段: 服務不可用;
第二階段:調用端重試加大流量(使用者重試/代碼邏輯重試);
第三階段:服務調用者不可用(同步等待造成的資源耗盡);
3、解決方案
1) 應用擴容(擴大伺服器承受力)
- 加機器
- 更新硬體
2)流量控制(超出限定流量,傳回類似重試頁面讓使用者稍後再試)
- 限流
- 關閉重試
3) 緩存
将使用者可能通路的資料大量的放入緩存中,減少通路資料庫的請求。
4)服務降級
- 服務接口拒絕服務
- 頁面拒絕服務
- 延遲持久化
- 随機拒絕服務
5) 服務熔斷
如果對服務降級和服務熔斷的概念模糊點此了解 關于服務熔斷和服務降級的詳解
二、Hystrix入門
Hystrix簡介
Hystrix [hɪst’rɪks],中文含義是豪豬,因其背上長滿棘刺,進而擁有了自我保護的能力。
以項目案例開始,快速入門(使用IDEA)
場景假設1( 服務提供方報錯) : 在服務提供端中因為通路不到資料庫中的資料(比如資料不存在,或是資料庫壓力過大,查詢請求隊列中),在這種情況下,服務提供方這邊如何實作服務降級,以防止服務雪崩.
- 使用IDEA建立一個 microservice-provider-hystrix 工程
- 因為此工程要受到Hystrix保護,是以加入依賴.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
-
在microservice-provider-hystrix 工程的啟動類上啟用斷路器
在啟動類上加入注解
@EnableCircuitBreaker //啟用斷路器
注意: 這裡其實也可以使用 spring cloud應用中的@SpringCloudApplication注解,因為它已經自帶了這些注解,源碼如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}
- 在 ProductController中加入斷路邏輯
@RequestMapping("/get/{id}")
@HystrixCommand(fallbackMethod="errorCallBack") //模仿沒有這個資料時,服務降級
public Object get(@PathVariable("id") long id){
Product p=this.productService.findById(id);
if( p==null){
throw new RuntimeException("查無此産品");
}
return p;
}
//指定一個降級的方法
public Object errorCallBack( @PathVariable("id") long id ){
return id+"不存在,error";
}
- 啟動provider服務後測試
小結: 服務降級 由服務提供方 處理
場景假設2: 因為網絡抖動,或服務端維護導緻的服務暫時不可用,此時是用戶端聯接不到伺服器,因為feign有重試機制,這樣會導緻系統長時間不響應,那麼在這種情況上如何通過 feign+hystrix 在服務的消費方實作服務熔斷(回退機制)呢?
首先确認一下我們使用的用戶端是 microservice-consumer-feign , feign中自帶了 hystrix,但并沒有啟動,是以要配置啟用hystrix,修改 application.yml
feign:
hystrix:
enabled: true
我們的服務消費方的feign操作接口位于 microservice-interface中,是以要在這裡配置
- 建立一個包 fallback,用于存回退處理類 IProductClientServiceFallbackFactory,這個類有出現請求異常時的處理
package com.yc.springcloud2.fallback;
import com.yc.springcloud2.bean.Product;
import com.yc.springcloud2.service.IProductClientService;
import feign.hystrix.FallbackFactory;
import java.util.List;
@Component //必須被spring 托管
public class IProductClientServiceFallbackFactory implements FallbackFactory<IProductClientService> {
@Override
public IProductClientService create(Throwable throwable) {
//這裡提供請求方法出問題時回退處理機制
return new IProductClientService(){
@Override
public Product getProduct(long id) {
Product p=new Product();
p.setProductId(999999999L);
p.setProductDesc("error");
return p;
}
@Override
public List<Product> listProduct() {
return null;
}
@Override
public boolean addPorduct(Product product) {
return false;
}
};
}
}
- 在業務接口上加入 fallbackFactory屬性指定異常處理類
@FeignClient(name="MICROSERVICE-PROVIDER-PRODUCT",
configuration = FeignClientConfig.class,
fallbackFactory = IProductClientServiceFallbackFactory.class) // 配置要按自定義的類FeignClientConfig
public interface IProductClientService {
- 啟動 microservice-consumer-feign用戶端進行測試, 在測試時,嘗試關閉生産端,看它是否回退 小結: 服務熔斷在消費端 處理
三、Hystrix技術點
設計目标:
1. 對來自依賴的延遲和故障進行防護和控制——這些依賴通常都是通過網絡通路的
2. 阻止故障的連鎖反應
3. 快速失敗并迅速恢複
4. 回退并優雅降級
5. 提供近實時的監控與告警
設計原則:
1. 防止任何單獨的依賴耗盡資源(線程)
2. 過載立即切斷并快速失敗,防止排隊
3. 盡可能提供回退以保護使用者免受故障
4. 使用隔離技術(例如隔闆,泳道和斷路器模式)來限制任何一個依賴的影響
5. 通過近實時的名額,監控和告警,確定故障被及時發現
6. 通過動态修改配置屬性,確定故障及時恢複
7. 防止整個依賴用戶端執行失敗,而不僅僅是網絡通信
Hystrix如何實作:
- 使用指令模式将所有對外部服務(或依賴關系)的調用包裝在HystrixCommand或HystrixObservableCommand對象中,并将該對象放在單獨的線程中執行;
- 每個依賴都維護着一個線程池(或信号量),線程池被耗盡則拒絕請求(而不是讓請求排隊)。
- 記錄請求成功,失敗,逾時和線程拒絕。
- 服務錯誤百分比超過了門檻值,熔斷器開關自動打開,一段時間内停止對該服務的所有請求。
- 請求失敗,被拒絕,逾時或熔斷時執行降級邏輯。
- 近實時地監控名額和配置的修改。
四、服務監控HystrixDashboard的使用
步驟:
- 建立一個子產品【microservice-consumer-hystrix-dashboard】
- pom.xml配置
<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>
- application.yml中配置端口
server:
port: 9001
- 建立啟動類,配置hystrix啟動
package com.yc.springcloud2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApp {
public static void main(String[] args) {
SpringApplication.run( HystrixDashboardApp.class,args);
}
}
啟動後可通過 http://localhost:9001/hystrix 通路此應用
dashboard有三種監控模式:
預設的叢集監控: http://turbine-hostname:port/turbine.stream
指定的叢集監控: http://turbine-hostname:port/turbine.stream?cluster=[clusterName]
單體應用的監控: http://hystrix-app:port/actuator/hystrix.stream
頁面上面的幾個參數局域:
最上面的輸入框: 輸入上面所說的三種監控方式的位址,用于通路具體的監控資訊頁面。
Delay: 該參數用來控制伺服器上輪詢監控資訊的延遲時間,預設2000毫秒。
Title: 該參數對應頭部标題Hystrix Stream之後的内容,預設會使用具體監控執行個體的Url。
注意點:
1.前兩種監控模式都是對叢集的監控,需要整合Turbine才能實作。
2.而單個服務執行個體的監控,從http://hystrix-app:port/actuator/hystrix.stream連接配接中可以看出,Hystrix Dashboard監控單節點執行個體需要通路執行個體的actuator/hystrix.stream接口,我們自然就需要為服務執行個體添加這個端點
- 在 [microservice-provider-hystrix] 中的pom中加入actuator監控子產品,以開啟監控相關的端點,并確定引入斷路器的依賴: spring-cloud-starter-hystrix
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 在 [microservice-provider-hystrix] 中修改 application.yml 檔案,加入管理終端配置
management:
endpoints:
web:
exposure:
include: '*'
需要注意的是在Spring Cloud Finchley 版本以前通路路徑是/hystrix.stream,如果是Finchley的話就需要加入上面的配置。因為spring Boot 2.0.x以後的actuator default配置隻暴露了info和health2個端點,這裡我們把所有端點開放,include: '*'代表開放所有端點
- 確定在 [microservice-provider-hystrix] 的啟動主類中使用@EnableCircuitBreaker 注解,開啟斷路器功能.
@SpringBootApplication
@EnableEurekaClient //啟動eureka用戶端
@EnableDiscoveryClient //啟用服務發現用戶端,以擷取目前provider的注冊資訊
@EnableCircuitBreaker //啟用斷路器
@SpringCloudApplication
public class ProductApp_hystrix {
public static void main(String[] args) {
SpringApplication.run( ProductApp_hystrix.class,args);
}
}
- 浏覽器通路http://localhost:8888/actuator/hystrix.stream會看到如下頁面,因為監控的執行個體( 即 【microservice-provider-hystrix]本身還沒有調用任何服務,是以監控端點也沒記錄任何資訊
- 新開一個浏覽器tab頁,通路下 http://localhost:8888/product/get/1,重新重新整理下剛才的頁面可以看到已經有資料傳回了,說明我們的埋點生效了 從浏覽器中的資訊可以看出這些資訊是剛剛請求微服務時所記錄的監控資訊,但是直接去看這些資訊肯定是不友好的(根本看不懂),是以Hystrix Dashboard就派上用場了
- 在Hystrix Dashboard中間這個輸入框中,填入=服務的監控位址,也就是http://admin:[email protected]:8888/actuator/hystrix.stream,點選Monitor Stream按鈕,就會跳轉到具體的監控頁面
- 多次請求http://localhost:8888/product/get/1,會發現監控頁面的資料也在實時的更新
小結:
以上我們可以使用dashboard對單個執行個體做資訊監控,但在分布式系統中,有很多執行個體要運維和監控,這樣要開啟多個視窗來監控,是以可以利用turbine和dashboard結合來對叢集監控.