💡上一篇介紹一個用於聲明式調用服務的組件Fegin,主要用於解決前面的服務調用與restTemplate耦合緊密的問題。 Spring Cloud認知學習(三):聲明式調用Feign的使用
💡這一篇介紹一個新的組件Hystrix,Hystrix是一個熔斷器,可以用於解決微服務調用中發送的服務熔斷和服務降級問題。
Hystrix
* Hystrix是服務熔斷器,用於解決服務熔斷和服務降級的情況。
服務熔斷和服務降級
- 主要是解決服務熔斷和服務降級的問題。
服務熔斷主要用了解決服務雪崩,我們先來介紹一下服務雪崩的概念。 - 服務雪崩:假如有很多個服務都需要調用A服務,但A突然卡住了,響應很慢,在高並發的情況下,此時A服務就會持有了過量的資源,而導致其他服務的資源不足,從而影響其他服務的使用,甚至可能傳播性地導致整個系統崩潰。
- 服務熔斷:熔斷的概念是什么呢?就是假如說你家里用了超大的功率的電器,你家的電閘就會為了避免造成危險而幫你跳閘斷電。對於服務雪崩也是這樣的,他會防止服務占用過量的資源。
原理:用戶的請求將不再直接調用服務,而是通過一個線程池來調用服務,當線程池中沒有空閑的線程時,就會返回一個簡單的執行結果(需要設定,可能是一個提示信息或者一個假數據)。 - 服務降級:當某一個主要的服務端的資源不夠的時候,可能此時其他的不太重要服務需要進行關閉來為他騰出空間(就好像與支付功能相比,換頭像這個功能就可以暫時為支付功能獻身了),這就是服務的降級。雖然降級了,但理論上還應該給消費者一個保底的回應,比如說返回提示說服務已經關閉。由於此時服務的提供者已經關閉了,所以這個判斷只能發生在服務的消費者中。
💡:對於上面的這兩種情況,如果是服務熔斷,既可以部署在消費者也可以部署在生產者,因為只是一個“等太久就不等待”的處理,這個等太久既可以事消費者判斷也可以事生產者判斷;如果是服務降級,由於此時服務提供者大多都關閉了,所以這時候Hystrix只能部署在消費者端。
💡:由於Hystrix其實就是對於錯誤情況的處理,服務的過度消耗資源(雪崩)和降級其實都是服務不可用,他們的處理在Hystrix其實都是一樣的。所以下面就根據Hystrix的部署位置來演示。
簡單使用示例:
下面的代碼可以參考:Hystrix簡單使用實驗
部署在服務提供者
這次我們修改模塊spring-cloud-user-service-8003
1.導入依賴:
<!--增加hystrix依賴 start-->
<!--舊版本的依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--增加hystrix依賴 end-->
2.修改UserController,給listUser增加一個Hystrix處理方法listUserByHystirx。
@HystrixCommand(fallbackMethod = "listUserByHystirx")
代表發生不可用的時候,就會調用listUserByHystirx來返回結果。
package com.progor.study.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.progor.study.entity.User;
import com.progor.study.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
// 由於返回json數據,懶得加注解@ResponseBody了,加個RestController
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/{id}")
public User getUser(@PathVariable Integer id) {
User user = userService.getUser(id);
if (user == null) {
throw new RuntimeException("該ID:" + id + "沒有對應的用戶信息");
}
return user;
}
private static int count = 0;
@GetMapping("/user/list")
// fallbackMethod時發生錯誤時調用的方法,
// commandProperties用於配置熔斷器,requestVolumeThreshold代表請求多少次就不再嘗試調用原方法,直接調用錯誤處理方法。
@HystrixCommand(fallbackMethod = "listUserByHystirx",commandProperties = {
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"), //請求次數
}) //
public List<User> listUser() throws InterruptedException {
count = count+1; // 假裝偶爾發生了bug
System.out.println(count);
// 偶爾發生時
// if (count%2 == 0){
// Thread.sleep(5000);
// }
// 一直發生時:
Thread.sleep(5000);
List<User> users = userService.listUser();
return users;
}
public List<User> listUserByHystirx() {
User user = new User(0,"null","null");
List<User> users = new ArrayList<>();
users.add(user);
return users;
}
}
<br>
3.在主程序類中開啟hystrix:
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix // 開啟hystrix
public class UserService8003Application {
public static void main(String[] args) {
SpringApplication.run(UserService8003Application.class, args);
}
}
4.啟動spring-cloud-user-service-8003
,訪問http://localhost:8003/user/list
,發現如果發生錯誤的時候,會調用listUserByHystirx來返回。
🔵如果你通過服務消費者來調用8003的服務的話,這時候也會一樣會在發生錯誤的時候,調用listUserByHystirx來返回。
🔵注意,我上面的代碼是一次執行成功,一次執行失敗這樣的順序。
部署在服務消費者
目前我們只有一個服務消費者,所以我們要修改spring-cloud-user-consumer-80
1.在spring-cloud-user-consumer-80
模塊導入依賴:
<!--增加hystrix依賴 start-->
<!--舊版本的依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--增加hystrix依賴 end-->
2.修改MessageController2,增加@HystrixCommand
// 這個控制器用來處理使用fegin的情況
@RestController
public class MessageController2 {
@Autowired
private MessageService messageService;
@GetMapping("/msg2/list")
// 使用HystrixCommand
@HystrixCommand(fallbackMethod = "listByHystirx",commandProperties = {
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"), //請求次數
}) //如果請求次數達到5次都是失敗,那么直接調用listByHystirx
public Map<String, String> list() {
return messageService.list();
}
public Map<String, String> listByHystirx() {
Map<String, String> map = new HashMap<>();
map.put("msg","服務端已停止服務");
return map;
}
}
3.修改主程序類,開始Hystrix,@EnableHystrix
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "USERSERIVE", configuration = MyConfig.class)
@EnableFeignClients // 使用feign
@EnableHystrix
public class UserConsumer80Application {
public static void main(String[] args) {
SpringApplication.run(UserConsumer80Application.class, args);
}
}
4.測試
- 💡啟動
spring-cloud-user-consumer-80
和spring-cloud-eureka-server-7001
- 💡訪問一次
http://localhost/msg2/list
。此時由於沒有服務實例,那么應該會調用listByHystirx來返回結果。- 請注意我們之前創建的MessageService在8004和8005,如果我們沒有啟動8004或8005,那么此時eureka內部應該沒有MessageService服務實例,所以你會發現會調用我們的錯誤處理方法中的結果來返回給我們,而如果我們調用
http://localhost/msg/list
由於我們沒有做hystrix處理,那么就會報錯;
- 請注意我們之前創建的MessageService在8004和8005,如果我們沒有啟動8004或8005,那么此時eureka內部應該沒有MessageService服務實例,所以你會發現會調用我們的錯誤處理方法中的結果來返回給我們,而如果我們調用
- 💡啟動
spring-cloud-message-service-8004
,訪問http://localhost/msg2/list
,是訪問成功的。【但由於我們設置了重試次數為5,如果你啟動了8004還是訪問失敗,那么嘗試重啟一下80吧,不然要等重新拉取eureka的信息(30s大概)。】 - 💡再停掉
spring-cloud-message-service-8004
,訪問http://localhost/msg2/list
,會調用listByHystirx來返回結果。
整合feign
🔵Hystrix還可以與Fegin整合,也是相當於部署在服務消費者。
下面的代碼可以參考:Hystrix整合Feign使用實驗
1.修改Feign代碼
由於我們只在spring-cloud-common-data
模塊整合了fegin,所以我們要在spring-cloud-common-data
做實驗了。
1.在spring-cloud-common-data
模塊導入依賴。
<!--增加hystrix依賴 start-->
<!--舊版本的依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--增加hystrix依賴 end-->
2.修改MessageService
,在FeignClient注解中增加fallback
// 由於這種服務的服務消費者可能比較多,放到共有依賴中。
// 使用fallback指定一個類,這個類實現了MessageService,發生服務不可用的時候就會調用這個類中方法
@FeignClient(value = "MESSAGESERIVE",fallback = MessageServiceHystrix.class)
public interface MessageService {
// 這里使用RequestMapping將服務提供者的方法與本地Service方法建立映射
@RequestMapping(value = "/msg/list", method = RequestMethod.GET)
Map<String, String> list();
}
3.創建MessageServiceHystrix:
package com.progor.study.service;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
// 注意,要使用@Component
@Component
public class MessageServiceHystrix implements MessageService {
@Override
public Map<String, String> list() {
Map<String, String> map = new HashMap<>();
map.put("msg","服務端已停止服務");
return map;
}
}
2.修改消費者
1.由於我們上面在MessageController2中使用了Hystrix,我們新建一個MessageController3:
@RestController
public class MessageController3 {
@Autowired
private MessageService messageService;
@GetMapping("/msg3/list")
public Map<String, String> list() {
return messageService.list();
}
}
2.修改spring-cloud-user-consumer-80
中的application.yml增加如下內容:
feign:
hystrix:
enabled: true # 用來開啟fegin中的hystrix
🔴注意,此時主程序類中的@EnableHystrix可以沒有
3.測試:
💡啟動spring-cloud-user-consumer-80
和spring-cloud-eureka-server-7001
,訪問http://localhost/msg2/list
- 💡訪問一次
http://localhost/msg3/list
。此時由於沒有服務實例,那么應該會調用listByHystirx來返回結果。- 請注意我們之前創建的MessageService在8004和8005,如果我們沒有啟動8004或8005,那么此時eureka內部應該沒有MessageService服務實例,所以你會發現會調用我們的錯誤處理方法中的結果來返回給我們,而如果我們調用
http://localhost/msg/list
由於我們沒有做hystrix處理,那么就會報錯;
- 請注意我們之前創建的MessageService在8004和8005,如果我們沒有啟動8004或8005,那么此時eureka內部應該沒有MessageService服務實例,所以你會發現會調用我們的錯誤處理方法中的結果來返回給我們,而如果我們調用
- 💡啟動
spring-cloud-message-service-8004
,訪問http://localhost/msg2/list
,是訪問成功的。【如果你啟動了8004還是訪問失敗,那么嘗試重啟一下80吧,不然要等重新拉取eureka的信息(30s大概)。】 - 💡再停掉
spring-cloud-message-service-8004
,訪問http://localhost/msg2/list
,會調用listByHystirx來返回結果。
Hystrix Dashboard
💡Hystrix Dashboard是一個監控Hystrix熔斷器狀況的組件,有圖形化的數據統計界面,你可以通過查看熔斷器的統計數據來判斷服務的狀況。
💡對於restTemplate和fegin整合Hystrix Dashboard的方式都是一樣的。
下面來演示整合,代碼可以參考:Hystrix Dashboard整合使用實驗,PS,這里的commit的注釋上一個版本提交錯了一個代碼,請以這個版本的為准
有點小問題,我以為我提交了一個錯誤的版本,沒想到我revert成功了,參考這個版本的代碼即可。上一個版本Hystrix整合Feign使用實驗
不存在錯誤。
1.配置啟動Hystrix Dashboard
0.創建模塊spring-cloud-hystrix-dashboard-9001
1.導入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
2.主程序類添加注解@EnableHystrixDashboard:
@SpringBootApplication
@EnableHystrixDashboard
public class SpringCloudHystrixDashboard9001Application {
public static void main(String[] args) {
SpringApplication.run(SpringCloudHystrixDashboard9001Application.class, args);
}
}
3.訪問http://localhost:9001/hystrix
,如果能正常看到頁面,那么就啟動成功了。
💡上面啟動了Hystrix Dashboard服務,但Hystrix Dashboard是一個接收服務的數據的組件,服務不開放數據,它也接收不了,下面會進行配置。
2.修改服務提供者
1.在服務提供者spring-cloud-user-service-8003中增加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.在spring-cloud-user-service-8003的主程序類中增加ServletRegistrationBean的Bean:【這個是在Finchley的時候增加的好像,以前可以不配這個Bean】
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix // 開啟hystrix
public class UserService8003Application {
public static void main(String[] args) {
SpringApplication.run(UserService8003Application.class, args);
}
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.addUrlMappings("/hystrix.stream");//路徑
return registrationBean;
}
}
3.修改8003的熔斷器處理代碼:
因為Hystrix dashboard只能檢測發生被熔斷器處理的方法。沒有熔斷處理的方法無法被監控。這里解開注釋,用於測試時好時壞時,對於Hystrix dashboard的成圖效果。
3.測試:
💡啟動spring-cloud-user-service-8003
,spring-cloud-eureka-server-7001
💡在谷歌瀏覽器中查看localhost:8003/hystrix.stream
【如果是在火狐,可能會進行下載。】,這是8003開放的請求流,Hystrix dashboard實際上就是對這個流來分析的。
當你訪問http://localhost:8003/user/list
的時候因為我們的代碼問題,它偶爾會發生問題,此時上圖的ping才會顯示出不同的內容。
💡訪問http://localhost:9001/hystrix
,是一個如下的頁面:
在中間輸入http://localhost:8003/hystrix.stream
,讓Hystrix Dashboard監控這個流,然后點擊下面的按鈕,你就進入一個這樣的圖:
Delay是監控的刷新時間。
7色:中間的7種顏色的,要參考右上角的顏色問題。
- Success:代表請求成功
- Short-Circuited:代表熔斷數
- Bad Request:代表拋出HystrixBadRequestException的次數
- Timeout:代表請求超時的次數
- Rejected:線程池拒絕數
- Failure:拋出異常的請求
- Error:最近10秒的錯誤比例
1圈:這個圈會隨着七種數字的總和而變大,圈越大,訪問越多;圈的顏色是其中顏色的混搭,比如成功為主的時候,偏於綠色,如果是偏於紅色,那么說明請求失敗很多。【綠、黃、橙、紅】
1線:線是請求次數的高低,沒有請求的時候就是一條直線,有請求的時候就是一張折線圖。
想了解圖中更多的內容可以參考:圖內數據官方參考文檔
補充:
- 更多內容(包括工作機制,多個Hystrix Dashboard基於Turbo的聚合情況。),將在單章說明,咕咕咕。