1.基本定義
Hystrix是一個用於處理分布式系統的延遲和容錯的開源庫,能夠保證在一個依賴出現問題的情況下,不會導致整體服務失敗,從而提高分布式系統的彈性。其功能有服務熔斷和降級等。
1.1扇出
多個微服務調用的時候,假設微服務A調用微服務B和C,微服務B和C又調用其他的服務,這就是扇出。
1.2服務雪崩
1)定義
如果扇出的鏈路上某個微服務的調用時間過長或不可用,導致級聯服務器發生故障的現象。其描述的是服務提供方不可用,導致服務消費方不可用並將不可用逐漸放大的過程。
2)圖解分析
若存在如下調用鏈路
若此時Service A的流量波動很大,流量經常會突然性增加,那么在這種情況下,就算Service A能扛得住請求,Service B和Service C未必能扛得住這突發的請求。如果Service C因為抗不住請求,變得不可用。那么Service B的請求也會阻塞,慢慢耗盡Service B的線程資源,Service B就會變得不可用。緊接着,Service A也會不可用,這一過程如下圖所示:
1.3斷路器
一種開關裝置,當某個服務單元發生故障后,通過斷路器的故障監控向調用方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或拋出異常。
當10秒內請求次數超過20次或h10秒內超過50%的請求是失敗的,斷路器都會自動打開。斷路器打開后,所有的請求都不會轉發,會返回配置的FallBack。一段時間后(默認秒),此時斷路器處於半開狀態,會讓其中的一個請求進行轉發,若成功則關閉斷路器,若失敗則繼續開啟斷路器。
1.4服務熔斷
對服務鏈路進行監控及熔斷。當扇出鏈路的某個微服務不可用火響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的調用,通過熔斷器快速返回錯誤的響應信息。當檢測到該節點微服務調用響應正常后恢復調用鏈路。
1.5服務降級
當服務壓力劇增的時候根據當前的業務情況及流量對一些服務和頁面有策略的降級,以此環節服務器的壓力,以保證核心任務的進行。同時保證部分甚至大部分任務客戶能得到正確的相應。也就是當前的請求處理不了了或者出錯了,給一個默認的返回。簡單的說,服務降級就是關閉系統的邊緣服務,保證核心服務的正常運行,從系統安全的季愛渡考慮的。
1.6服務熔斷和降級的區別
1)相同點
都從可用性着想,為了防止系統奔潰,讓用戶體驗到某些功能是不可用的
2)不同點
其觸發原因不同,熔斷是某個下游服務故障引起,降級是從系統負荷考慮。另外它們的管理目標層次不一樣,熔斷是框架級別的處理,每個微服務都會有,而降級是針對具體的業務層級。
熔斷一定觸發降級,故熔斷也是降級的一種。區別在於熔斷是對調用鏈路的保護,而降級是對系統過載的保護處理。
2.項目實戰
源碼:https://github.com/zhongyushi-git/cloud-hystrix-demo.git
2.1基礎環境搭建
1)前言
由於只是說明其用法,故本文並未對服務提供者進行大量並發請求從而導致服務雪崩現象。制造異常進行熔斷和服務雪崩進行熔斷的原理是相似的,故本文是通過在服務提供放制造異常,從而導致服務調用方服務異常的現象進行演示,然后對服務提供方進行熔斷從而服務調用方正常使用。
2)創建一個maven工程名為cloud-hystrix-demo
,刪除src目錄
3)在pom中導入依賴,對SpringBoot和SpringCloud版本進行鎖定
<properties> <spring.boot.version>2.2.2.RELEASE</spring.boot.version> <spring.cloud.version>Hoxton.SR1</spring.cloud.version> </properties> <!-- 依賴管理,父工程鎖定版本--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <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>
2.2搭建服務提供者
1)新建maven子模塊(cloud-provider8001),導入依賴
<dependencies> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
2)新建啟動類ProviderMain8001並添加注解
package com.zys.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderMain8001 { public static void main(String[] args) { SpringApplication.run(ProviderMain8001.class, args); } }
3)配置application.yml
server:
port: 8001 spring: application: name: cloud-consul-provider cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name}
4)新建controller接口
package com.zys.cloud.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Value("${server.port}") private String port; @GetMapping("/user/get") public String get() { int i = 10 / 0; return "我是服務提供者,端口:" + port; } }
這里專門在接口里加了會發生異常的代碼(除數為0的異常)。
2.3搭建服務消費者
1)新建maven子模塊(cloud-consumer80),導入依賴
<dependencies> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
2)新建啟動類ConsumerMain80並添加注解
package com.zys.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients class ConsumerMain80 { public static void main(String[] args) { SpringApplication.run(ConsumerMain80.class, args); } }
3)配置application.yml
server: port: 80 spring: application: name: cloud-consul-consumer cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name} feign: client: config: #指定全局 default: #連接超時時間 connectTimeout: 5000 #服務等待時間 readTimeout: 5000 loggerLevel: full logging: level: com.zys.cloud.service.UserServiceClient: debug
3)創建服務接口UserServiceClient,對於服務提供者接口
package com.zys.cloud.service; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; //指定微服務名稱 @FeignClient(value = "cloud-consul-provider") public interface UserServiceClient { @GetMapping("/user/get") String get(); }
4)創建controller接口,將UserServiceClient注入使用
package com.zys.cloud.controller; import com.zys.cloud.service.UserServiceClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @RequestMapping("/consumer") public class TestController { @Resource private UserServiceClient userServiceClient; @GetMapping("/get") public String get() { return userServiceClient.get(); } }
5)啟動測試。先啟動服務提供者集群,再啟動服務消費者。訪問http://localhost/consumer/get,發現出現了500:
報錯的原因很簡單,因為在服務提供者中出現了除數為0的異常,導致服務消費者也跟着發生異常,也就是上述的服務雪崩現象。
2.4對服務提供方進行服務熔斷
1)在cloud-provider8001的pom中加入Hystrix的依賴
<!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2)在啟動類上添加注解@EnableCircuitBreaker,啟動熔斷器
3)對異常的方法進行錯誤的回調
在異常的方法上添加注解@HystrixCommand,指定回調處理的方法,並自定義回調的方法
@GetMapping("/user/get") @HystrixCommand(fallbackMethod = "getFallBack") public String get() { int i = 10 / 0; return "我是服務提供者,端口:" + port; } //回調方法 public String getFallBack() { return "當前訪問人數較多,請稍后再試"; }
這里是通過設置fallbachMethod
對每個要處理的方法設置回調的方法,也就意味着要對接口的每個需要處理的方法提供一個回調方法。當然也可以給接口的方法設置一個統一的回調方法,通過設defaultFallback
,那么在其他需要處理的方法加上注解@HystrixCommand並指定defaultFallback即可:
4)啟動測試。重啟cloud-provider8001,再次訪問http://localhost/consumer/get,服務正常調用,顯示回調的錯誤信息
2.5對服務消費方進行服務降級
除了在服務提供方進行服務熔斷外,還可以在服務消費方進行服務降級。
1)在服務消費者中導入hystrix依賴
<!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2)在服務消費者中yml開啟feign的熔斷
feign: #開啟服務降級 hystrix: enabled: true
3)在服務消費者新建fallback處理類並實現UserServiceClient
package com.zys.cloud.fallback; import com.zys.cloud.service.UserServiceClient; import org.springframework.stereotype.Component; @Component public class UserFallBack implements UserServiceClient { @Override public String get() { return "服務提供方不可用,請稍后再試"; } }
若有多個方法,則需要對每個方法都重寫來設置降級的信息。
4)在服務消費者的服務客戶端UserServiceClient的注解FeignClient指定fallback的處理類
5)在服務消費者啟動類上添加注解@EnableHystrix
6)重啟服務消費者服務后,再次訪問http://localhost/consumer/get,顯示信息
你可能發現了,服務消費方已經配置了服務降級,為何還是顯示服務提供方的回調信息?原因是服務提供方的服務熔斷級別優先於服務消費方的服務降級。
當在啟動時若提示程序包org.springframework.boot不存在
,那么需要對IDEA進行配置:
打開設置,按下圖進行勾選
作用是將IDE構建或運行操作委托給Maven,勾選后啟動就不會報錯了。
7)把服務消費者服務停掉,再次訪問http://localhost/consumer/get,顯示信息
此時顯示的便是服務降級的信息,服務端報錯或停止后不會導致服務消費方停止服務。
注意:如果服務端降級和客戶端降級同時開啟,要求服務端降級方法的返回值必須與客戶端方法降級的返回值一致。
2.6服務監控
對於微服務,也是需要進行監控的。Hystrix也提供了准實時的服務監控(Hystrix Dashboard),它會持續的記錄所有通過Hystrix發起的請求的執行信息,並以統計報表和圖形的形式展現給用戶。
在上述的基礎上繼續開發:
1)新建maven子模塊(cloud-dashboard),導入依賴
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
2)配置application.yml文件
server: port: 9001
3)創建啟動類並添加注解@EnableHystrixDashboard
package com.zys.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @SpringBootApplication @EnableHystrixDashboard public class HystrixDashboardMain9001 { public static void main(String[] args) { SpringApplication.run(HystrixDashboardMain9001.class, args); } }
4)在服務提供者中添加配置類
package com.zys.cloud.config; import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class HystrixConfig { @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); //指定監控路徑 registrationBean.addUrlMappings("/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; } }
此配置類的作用是指定監控的路徑。
5)先啟動服務監控9001,然后服務提供者8001。然后在瀏覽器輸入http://localhost:9001/hystrix,可以看到下面的界面,說明配置成功。
6)在第一個輸入框輸入http://localhost:8001/hystrix.stream,點擊下面的按鈕,可以進入服務的監控頁面
7)快速調用幾次服務消費者的接口http://localhost/consumer/get,再回到監控頁面看到有圖形在實時變化,這就是實時的監控效果。