Spring Cloud認知學習(四):熔斷器Hystrix的使用



💡上一篇介紹一個用於聲明式調用服務的組件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-80spring-cloud-eureka-server-7001
  • 💡訪問一次http://localhost/msg2/list。此時由於沒有服務實例,那么應該會調用listByHystirx來返回結果。
    • 請注意我們之前創建的MessageService在8004和8005,如果我們沒有啟動8004或8005,那么此時eureka內部應該沒有MessageService服務實例,所以你會發現會調用我們的錯誤處理方法中的結果來返回給我們,而如果我們調用http://localhost/msg/list由於我們沒有做hystrix處理,那么就會報錯;
  • 💡啟動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-80spring-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處理,那么就會報錯;
  • 💡啟動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的成圖效果。
20200512231547



3.測試:

💡啟動spring-cloud-user-service-8003spring-cloud-eureka-server-7001
💡在谷歌瀏覽器中查看localhost:8003/hystrix.stream【如果是在火狐,可能會進行下載。】,這是8003開放的請求流,Hystrix dashboard實際上就是對這個流來分析的。

當你訪問http://localhost:8003/user/list的時候因為我們的代碼問題,它偶爾會發生問題,此時上圖的ping才會顯示出不同的內容。
💡訪問http://localhost:9001/hystrix,是一個如下的頁面:
20200512234305
在中間輸入http://localhost:8003/hystrix.stream,讓Hystrix Dashboard監控這個流,然后點擊下面的按鈕,你就進入一個這樣的圖:
Delay是監控的刷新時間。



7色:中間的7種顏色的,要參考右上角的顏色問題。

  • Success:代表請求成功
  • Short-Circuited:代表熔斷數
  • Bad Request:代表拋出HystrixBadRequestException的次數
  • Timeout:代表請求超時的次數
  • Rejected:線程池拒絕數
  • Failure:拋出異常的請求
  • Error:最近10秒的錯誤比例
    1圈:這個圈會隨着七種數字的總和而變大,圈越大,訪問越多;圈的顏色是其中顏色的混搭,比如成功為主的時候,偏於綠色,如果是偏於紅色,那么說明請求失敗很多。【綠、黃、橙、紅】
    20200512234416
    1線:線是請求次數的高低,沒有請求的時候就是一條直線,有請求的時候就是一張折線圖。
    想了解圖中更多的內容可以參考:圖內數據官方參考文檔


補充:

  • 更多內容(包括工作機制,多個Hystrix Dashboard基於Turbo的聚合情況。),將在單章說明,咕咕咕。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM