Spring Cloud Hystrix的學習

前言

分佈式系統面臨的問題

複雜分佈式體系結構中的應用程序有許多依賴項,每個依賴項在某些時候都不可避免地會失敗。如果主機應用程序沒有與這些外部故障隔離,那麼它有可能被他們拖垮。

當一切正常時,某一個服務依賴其他的服務

Spring Cloud Hystrix的學習

當其中有一個系統有延遲時,它可能阻塞整個用戶請求:

Spring Cloud Hystrix的學習

在高流量的情況下,一個後端依賴項的延遲可能導致所有服務器上的所有資源在數秒內飽和(PS:意味著後續再有請求將無法立即提供服務)

Spring Cloud Hystrix的學習

在微服務架構體系下,服務間的調用錯綜複雜,交織成一張大網。如果其中某個節點突然無法正常工作,則訪問它的眾多服務都會被卡住,進而有更多服務被卡住,系統中的線程、CPU、內存等資源有可能被迅速耗盡,最終整個服務體系崩潰。我們管這樣的現象叫服務雪崩。

Hytrix

Hystrix 是一個用於處理分佈式系統的延遲和容錯開源庫,在分佈式系統裡,許多依賴不可避免的會調用失敗,比如超時、異常等,Hystrix 能夠保證在一個依賴出問題的情況下,不會導致整體服務失敗,避免級聯故障,以提高分佈式系統的彈性。

斷路器”本身是一種開關裝置,當某個服務單元發生故障之後,通過斷路器的故障監控(類似熔斷保險絲),向調用方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者拋出調用方無法處理的異常,這樣就保證了服務調用方的線程不會被長時間、不必要地佔用,從而避免了故障在分佈式系統中的蔓延,乃至雪崩。

Hytrix 能夠提供服務降級、服務熔斷、服務限流接近實時的監控等方面的功能。

熔斷機制

熔斷機制是應對雪崩效應的一種微服務鏈路保護機制。

當篩出鏈路的某個微服務不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的調用,快速響應錯誤信息。當檢測到該節點微服務調用響應正常後恢復調用鏈路。在 SpringCloud 框架裡熔斷機制通過 Hystrix 實現。Hystrix 會監控微服務間調用的狀況,當失敗的調用到一定閾值,缺省是 5 秒內 20 次調用失敗就會啟動熔斷機制。熔斷機制的註解是@HystrixCommand。

項目搭建示例:

Spring Cloud Hystrix的學習

在Provider添加Hystrix依賴

<code><dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>/<code>

啟動類添加斷路器EnableCircuitBreaker註解,開啟啟用斷路器功能

<code>@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ProviderApplication {

\tpublic static void main(String[] args) {
\t\tSpringApplication.run(ProviderApplication.class, args);
\t}
}/<code>

Common端,添加統一返回的數據封裝類

<code>public class ResultEntity<T> {

\tpublic static final String SUCCESS = "SUCCESS";
\tpublic static final String FAILED = "FAILED";
\tpublic static final String NO_MESSAGE = "NO_MESSAGE";
\tpublic static final String NO_DATA = "NO_DATA";
\t/**
\t * 操作成功,不需要返回數據
\t * @return
\t */
\tpublic static ResultEntity<String> successWithoutData() {
\t\treturn new ResultEntity<String>(SUCCESS, NO_MESSAGE, NO_DATA);
\t}
\t/**
\t * 操作成功,需要返回數據
\t * @param data * @return
\t */
\tpublic static <E> ResultEntity<E> successWithData(E data) {
\t\treturn new ResultEntity<>(SUCCESS, NO_MESSAGE, data);
\t}

\t/**
\t * 操作失敗,返回錯誤消息
\t * @param message * @return
\t */
\tpublic static <E> ResultEntity<E> failed(String message) {
\t\treturn new ResultEntity<>(FAILED, message, null);
\t}
\tprivate String result;
\tprivate String message;
\tprivate T data;
\tpublic ResultEntity() {
// TODO Auto-generated constructor stub
\t}
\tpublic ResultEntity(String result, String message, T data) {

\t\tsuper();
\t\tthis.result = result;
\t\tthis.message = message;
\t\tthis.data = data;
\t}
\t@Override
\tpublic String toString() {
\t\treturn "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
\t}
\tpublic String getResult() {
\t\treturn result;
\t}
\tpublic void setResult(String result) {
\t\tthis.result = result;
\t}
\tpublic String getMessage() {
\t\treturn message;
\t}
\tpublic void setMessage(String message) {
\t\tthis.message = message;
\t}
\tpublic T getData() {
\t\treturn data;
\t}
\tpublic void setData(T data) {
\t\tthis.data = data;
\t}
}/<code>

Provider端

<code>// @HystrixCommand 註解通過 fallbackMethod 屬性指定斷路情況下要調用的備份方法\t 

@HystrixCommand(fallbackMethod = "getEmpBackup")
\t@RequestMapping("/provider/circuit/breaker/get/emp")
\tpublic ResultEntity<Employee> getEmp(@RequestParam("signal") String signal) {
\t\tif ("exception".equals(signal)) {
\t\t\tthrow new RuntimeException();
\t\t}
\t\tif ("timeout".equals(signal)) {
\t\t\ttry {
\t\t\t\tThread.sleep(5000);
\t\t\t} catch (InterruptedException e) {
\t\t\t\te.printStackTrace();
\t\t\t}
\t\t}
\t\treturn ResultEntity.successWithData(new Employee(666, "sam666", BigDecimal.valueOf(666.66)));
\t}


\tpublic ResultEntity<Employee> getEmpBackup(@RequestParam("signal") String signal) {
\t\treturn ResultEntity.failed("circuit break workded,with signal=" + signal);
\t}/<code>

啟動eureka,provider。啟動成功後,瀏覽器輸入:

http://localhost:1000/provider/circuit/breaker/get/emp?signal=111,表示服務正常。

Spring Cloud Hystrix的學習

http://localhost:1000/provider/circuit/breaker/get/emp?signal=exception,模擬服務出現異常


Spring Cloud Hystrix的學習

跳轉備選服務,getEmpBackup。

http://localhost:1000/provider/circuit/breaker/get/emp?signal=timeout,模擬請求服務超時,


Spring Cloud Hystrix的學習

跳轉備選服務,getEmpBackup。

降級機制

服務降級處理是在客戶端(Consumer 端)實現完成的,與服務端(Provider 端)沒有關係。當某個 Consumer 訪問一個 Provider 卻遲遲得不到響應時執行預先設定好的一個解決方案,而不是一直等待。

搭建原理示例

Spring Cloud Hystrix的學習

common端添加依賴:

<code><dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>/<code>

配置FallbackFactory

<code>/**
* 請注意自動掃描包的規則
* 比如:feign-consumer 工程需要使用 MyFallBackFactory,
* 那麼 MyFallBackFactory 應該在feign-consumer 工程的主啟動類所在包或它的子包下
* 簡單來說:哪個工程用這個類,哪個工程必須想辦法掃描到這個類
*實現Consumer端服務降級功能
* 實現FeignClientFactory接口時要傳入@FeignClient註解接口的類型
*在create()方法中返回@FeignClient註解標記類型的對象,當Provider
* 調用失敗,會執行這個對象對應的方法
* **/
@Component
public class MyFallBackFactory implements FallbackFactory<EmployeeRemoteService> {

\tpublic EmployeeRemoteService create(final Throwable cause) {
\t\treturn new EmployeeRemoteService() {

//// cause 對象是失敗原因對應的異常對象
\t\t\t@RequestMapping("/provider/get/employee/remote")
\t\t\tpublic Employee getEmployeeRemote() {
\t\t\t\treturn null;
\t\t\t}

\t\t\tpublic ResultEntity<Employee> getEmp(String signal) {
\t\t\t\treturn ResultEntity.failed(return ResultEntity.failed("降級機制生效:"+cause.getMessage()););
\t\t\t}
\t\t};
\t}



}/<code>

common的api端添加調用的方法getEmp

<code>/**
* FeignClient註解表示當前接口和一個Provider對應,註解中value屬性指定要
* 調用的Provider的微服務名稱
* 註解中的fallbackFactory屬性指定Provider不可用時提供備用方案的工廠類型
* */
@FeignClient(value = "springcloud-provider",fallbackFactory = MyFallBackFactory.class)
public interface EmployeeRemoteService {

\t@RequestMapping("/provider/get/employee/remote")
\tpublic Employee getEmployeeRemote();

\t@RequestMapping("/provider/circuit/breaker/get/emp")
\tpublic ResultEntity<Employee> getEmp(@RequestParam("signal") String signal);

}/<code>

provider的getEmp方法

<code>@RestController
public class EmployeeController {

\t@RequestMapping("/provider/get/employee/remote")
\tpublic Employee getEmployeeRemote() {
\t\treturn new Employee(12, "Tom", BigDecimal.valueOf(4933.55));
\t}

\t@HystrixCommand(fallbackMethod = "getEmpBackup")
\t@RequestMapping("/provider/circuit/breaker/get/emp")
\tpublic ResultEntity<Employee> getEmp(@RequestParam("signal") String signal) {
\t\tif ("exception".equals(signal)) {
\t\t\tthrow new RuntimeException();
\t\t}
\t\tif ("timeout".equals(signal)) {
\t\t\ttry {
\t\t\t\tThread.sleep(5000);
\t\t\t} catch (InterruptedException e) {
\t\t\t\te.printStackTrace();
\t\t\t}

\t\t}
\t\treturn ResultEntity.successWithData(new Employee(666, "sam666", BigDecimal.valueOf(666.66)));
\t}


\tpublic ResultEntity<Employee> getEmpBackup(@RequestParam("signal") String signal) {
\t\treturn ResultEntity.failed("circuit break workded,with signal=" + signal);
\t}/<code>

feign-consumer端添加配置

<code>feign:
hystrix:
enabled: true/<code>

controller添加調用的方法

<code>@RequestMapping("/feign/consumer/test/fallback")
\tpublic ResultEntity<Employee> testFallBack(@RequestParam("signal")String signal) {
\t\treturn employeeRemoteService.getEmp(signal);
\t}/<code>

啟動springcloud-eureka,springcloud-provider,springcloud-feign-consumer

瀏覽器訪問:http://localhost:7000/feign/consumer/test/fallback?signal=111,訪問正常


Spring Cloud Hystrix的學習


關閉Provider,再次訪問

Spring Cloud Hystrix的學習

降級生效,執行降級方法。再次啟動Provider,又可以訪問。

監控

新建一個springcloud-dashboard模塊。

<code><dependency>
<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>/<code>

啟動類:

<code>// 啟用 Hystrix 儀表盤功能
@EnableHystrixDashboard
@SpringBootApplication
public class DashBoardApplication {

\tpublic static void main(String[] args) {
\t\tSpringApplication.run(DashBoardApplication.class, args);
\t}
}/<code>

配置文件:

<code>server:
port: 8000

spring:
application:
name: springcloud-dashboard/<code>

對provider模塊進行監控,添加依賴:

<code><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>/<code>

添加配置:

<code>management:
endpoints:
web:
exposure:
include: hystrix.stream/<code>

啟動項目,瀏覽器輸入localhost:8000/hystrix

Spring Cloud Hystrix的學習

查看監控數據

直接查看監控數據本身

http://localhost:1000/actuator/hystrix.stream,

  • 說明 1:http://localhost:1000 訪問的是被監控的 provider 工程
  • 說明 2:/actuator/hystrix.stream 是固定格式
  • 說明 3:如果從 provider 啟動開始它的方法沒有被訪問過,那麼顯示的數據只有“ping:”,要實際訪問一個帶熔斷功能的方法才會有實際數據。

先請求一次才有數據。http://localhost:7000/feign/consumer/test/fallback?signal=111,在訪問

http://localhost:1000/actuator/hystrix.stream。

Spring Cloud Hystrix的學習

通過儀表盤工程訪問監控數據

第一步:打開儀表盤工程的首頁

http://localhost:8000/hystrix

第二步:填入獲取監控數據的地址(上面直接查看時使用的地址)

Spring Cloud Hystrix的學習

這時,可以不斷刷新之前的熔斷的請求,可以看出監控界面數據實時的變化。

http://localhost:1000/provider/circuit/breaker/get/emp?signal=exception

Spring Cloud Hystrix的學習


分享到:


相關文章: