1.Hystrix斷路器
1.1分布式系統面臨的問題
複雜分布式體系結構中的應用程式有數十個依賴關系,每個依賴關系在某些時候将不可避免地失敗。
-
服務雪崩
多個微服務之間調用的時候,假設微服務A調用微服務B和微服務C,微服務B和微服務C又調用其它的微服務,這就是所謂的“扇出”。如果扇出的鍊路上某個微服務的調用響應時間過長或者不可用,對微服務A的調用就會占用越來越多的系統資源,進而引起系統崩潰,所謂的“雪崩效應”.
示例:
在微服務架構中通常會有多個服務層調用,基礎服務的故障可能會導緻級聯故障,進而造成整個系統不可用的情況,這種現象被稱為服務雪崩效應。服務雪崩效應是一種因“服務提供者”的不可用導緻“服務消費者”的不可用,并将不可用逐漸放大的過程。
如下圖所示:A作為服務提供者,B為A的服務消費者,C和D是B的服務消費者。A不可用引起了B的不可用,并将不可用像滾雪球一樣放大到C和D時,雪崩效應就形成了。
1.2 Hystrix概述
Hystrix [hɪst’rɪks]的中文含義是豪豬,因其背上長滿了刺而擁有自我保護能力。
Hystix,即熔斷器。
首頁:https://github.com/Netflix/Hystrix/
- Hystrix是一個用于處理分布式系統的延遲和容錯的開源庫,在分布式系統裡,許多依賴不可避免的會調用失敗,比如逾時、異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導緻整體服務失敗,避免級聯故障,以提高分布式系統的彈性。
- “斷路器”本身是一種開關裝置,當某個服務單元發生故障之後,通過斷路器的故障監控(類似熔斷保險絲),向調用方傳回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者抛出調用方無法處理的異常,這樣就保證了服務調用方的線程不會被長時間、不必要地占用,進而避免了故障在分布式系統中的蔓延,乃至雪崩。
1.3.熔斷器的工作機制:
正常工作的情況下,用戶端請求調用服務API接口:
當有服務出現異常時,直接進行失敗復原,服務降級處理:
當服務繁忙時,如果服務出現異常,不是粗暴的直接報錯,而是傳回一個友好的提示,雖然拒絕了使用者的通路,但是會傳回一個結果。
這就好比去買魚,平常超市買魚會額外贈送殺魚的服務。等到逢年過節,逾時繁忙時,可能就不提供殺魚服務了,這就是服務的降級。
系統特别繁忙時,一些次要服務暫時中斷,優先保證主要服務的暢通,一切資源優先讓給主要服務來使用,在雙十一、618時,京東天貓都會采用這樣的政策。
1.4 Hystrix主要功能
- 服務降級
- 服務熔斷
- 服務限流
- 接近實時的監控
- …
2. 服務降級
2.1.服務降級概述
整體資源快不夠了,忍痛将某些服務先關掉,待渡過難關,再開啟回來。服務降級處理是在用戶端實作完成的,與服務端沒有關系。
Fallback相當于是降級操作。對于查詢操作,我們可以實作一個fallback方法,當請求後端服務出現異常的時候,可以使用fallback方法傳回的值。 fallback方法的傳回值一般是設定的預設值或者來自緩存。
2.2.引入依賴
首先在user-consumer中引入Hystix依賴:
<!--服務熔斷元件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.3.修改之前的Controller
在之前的Controller中添加熔斷機制:
@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
// 一旦調用服務方法失敗并抛出了錯誤資訊後,會自動調用@HystrixCommand标注好的fallbackMethod調用類中的指定方法
@HystrixCommand(fallbackMethod = "processHystrix_Get")
public User get(@PathVariable("id") Long id) {
User u = this.userService.get(id);
if (null == u) {
throw new RuntimeException("該ID:" + id + "沒有沒有對應的資訊");
}
return u;
}
public User processHystrix_Get(@PathVariable("id") Long id) {
User u=new User();
u.setId(110);
u.setUsername("該ID:" + id + "沒有沒有對應的資訊,null--@HystrixCommand");
u.setNote("no this database in MySQL");
return u;
}
2.4.修改主啟動類
修改consumer并添加新注解
@EnableCircuitBreaker
@SpringBootApplication
@EnableDiscoveryClient // 開啟EurekaClient功能
@EnableFeignClients // 開啟Feign功能
@EnableCircuitBreaker//對hystrixR熔斷機制的支援
public class SpringcloudDemoConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudDemoConsumerApplication.class, args);
}
}
2.5. 服務熔斷測試
- 3個eureka先啟動
- 主啟動類SpringcloudDemoConsumerApplication
-
通路測試
http://127.0.0.1:88/consumer/get/2
如果對應的ID:3,資料庫裡面沒有這個記錄,我們報錯後統一傳回。
3. 服務降級優化
-
修改microservicecloud-api工程,
根據已經有的DeptClientService接口建立一個實作了
FallbackFactory接口的類DeptClientServiceFallbackFactory
/**
* @author bruceliu
* @create 2019-08-04 15:11
* @description
*/
@Component // 不要忘記添加
public class UserClientServiceFallbackFactory implements FallbackFactory<UserClientService> {
@Override
public UserClientService create(Throwable throwable) {
return new UserClientService() {
@Override
public List<User> queryUsers() {
return null;
}
@Override
public User get(Long id) {
User u=new User();
u.setId(110);
u.setUsername("該ID:\" + id + \"沒有沒有對應的資訊,null--服務降級~~");
u.setNote("no this database in MySQL----服務降級!!!");
return u;
}
};
}
}
- 修改consumer工程,UserClientService接口在注解@FeignClient中添加fallbackFactory屬性值
/**
* @author bruceliu
* @create 2019-05-04 18:49
* @description Feign用戶端
*/
@FeignClient(value = "SPRINGCLOUD-DEMO-SERVICE",fallbackFactory=UserClientServiceFallbackFactory.class)
public interface UserClientService {
@RequestMapping("/all")
public List<User> queryUsers();
@RequestMapping("/get/{id}")
public User get(@PathVariable("id") Long id);
}
- 修改配置檔案
# 開啟服務熔斷政策
feign.hystrix.enabled=true
3.3. 測試
- 3個eureka先啟動
- 微服務提供者啟動
-
微服務消費者啟動
正常通路測試:http://127.0.0.1:88/consumer/get/1
故意關閉微服務提供者用戶端自己調用提示
此時服務端provider已經down了,但是我們做了服務降級處理,讓用戶端在服務端不可用時也會獲得提示資訊而不會挂起耗死伺服器。
5.Hystrix服務熔斷
5.1.服務熔斷概述
熔斷機制是應對雪崩效應的一種微服務鍊路保護機制。
斷路器很好了解,當Hystrix Command請求後端服務失敗數量超過一定比例(預設50%),斷路器會切換到開路狀态(Open)。這時所有請求會直接失敗而不會發送到後端服務。 斷路器保持在開路狀态一段時間後(預設10秒),自動切換到半開路狀态(HALF-OPEN)。這時會判斷下一次請求的傳回情況, 如果請求成功,斷路器切回閉路狀态(CLOSED), 否則重新切換到開路狀态(OPEN)。 Hystrix的斷路器就像我們家庭電路中的保險絲,一旦後端服務不可用,斷路器會直接切斷請求鍊,避免發送大量無效請求影響系統吞吐量, 并且斷路器有自我檢測并恢複的能力。
熔斷器開關互相轉換的邏輯圖:
在SpringCloud架構裡熔斷機制通過Hystrix實作。Hystrix會監控微服務間調用的狀況,當失敗的調用到一定門檻值,預設是5秒内20次調用失敗就會啟動熔斷機制。熔斷機制的注解是
@HystrixCommand
。
我們來說說斷路器的工作原理。當我們把服務提供者eureka-client中加入了模拟的時間延遲之後,在服務消費端的服務降級邏輯因為hystrix指令調用依賴服務逾時,觸發了降級邏輯,但是即使這樣,受限于Hystrix逾時時間的問題,我們的調用依然很有可能産生堆積。
這個時候斷路器就會發揮作用,那麼斷路器是在什麼情況下開始起作用呢?這裡涉及到斷路器的三個重要參數:快照時間窗、請求總數下限、錯誤百分比下限。這個參數的作用分别是:
快照時間窗:斷路器确定是否打開需要統計一些請求和錯誤資料,而統計的時間範圍就是快照時間窗,預設為最近的10秒。
請求總數下限:在快照時間窗内,必須滿足請求總數下限才有資格根據熔斷。預設為20,意味着在10秒内,如果該hystrix指令的調用此時不足20次,即時所有的請求都逾時或其他原因失敗,斷路器都不會打開。
錯誤百分比下限:當請求總數在快照時間窗内超過了下限,比如發生了30次調用,如果在這30次調用中,有16次發生了逾時異常,也就是超過50%的錯誤百分比,在預設設定50%下限情況下,這時候就會将斷路器打開。
那麼當斷路器打開之後會發生什麼呢?我們先來說說斷路器未打開之前,對于之前那個示例的情況就是每個請求都會在當hystrix逾時之後傳回fallback,每個請求時間延遲就是近似hystrix的逾時時間,如果設定為5秒,那麼每個請求就都要延遲5秒才會傳回。當熔斷器在10秒内發現請求總數超過20,并且錯誤百分比超過50%,這個時候熔斷器打開。打開之後,再有請求調用的時候,将不會調用主邏輯,而是直接調用降級邏輯,這個時候就不會等待5秒之後才傳回fallback。通過斷路器,實作了自動地發現錯誤并将降級邏輯切換為主邏輯,減少響應延遲的效果。
在斷路器打開之後,處理邏輯并沒有結束,我們的降級邏輯已經被成了主邏輯,那麼原來的主邏輯要如何恢複呢?對于這一問題,hystrix也為我們實作了自動恢複功能。當斷路器打開,對主邏輯進行熔斷之後,hystrix會啟動一個休眠時間窗,在這個時間窗内,降級邏輯是臨時的成為主邏輯,當休眠時間窗到期,斷路器将進入半開狀态,釋放一次請求到原來的主邏輯上,如果此次請求正常傳回,那麼斷路器将繼續閉合,主邏輯恢複,如果這次請求依然有問題,斷路器繼續進入打開狀态,休眠時間窗重新計時。
通過上面的一系列機制,hystrix的斷路器實作了對依賴資源故障的端口、對降級政策的自動切換以及對主邏輯的自動恢複機制。這使得我們的微服務在依賴外部服務或資源的時候得到了非常好的保護,同時對于一些具備降級邏輯的業務需求可以實作自動化的切換與恢複,相比于設定開關由監控和運維來進行切換的傳統實作方式顯得更為智能和高效。
6.服務降級與熔斷的差別
下面通過一個日常的故事來說明一下什麼是服務降級,什麼是熔斷。
故事的背景是這樣的:由于小強在工作中碰到一些問題,于是想請教一下業界大牛小壯。于是發生了下面的兩個場景: