spring cloud 入門12-Eureka斷路器——Hystrix

在我們的cloud中,各個節點之間相互配合、互相支持,大家齊心協力完成工作,但是天佑不測風雲,可能存在某一個微服務的某個時刻壓力變大導致服務緩慢,甚至出現故障,導致服務不能響應,例如:我們的網站推出新的營銷方案,註冊即得200元現金,這個時候註冊這個節點出現壓力過大,服務響應速度變緩,進入癱瘓狀態。

而這個時候產品微服務響應還是正常響應。但是如果出現產品微服務大量調用用戶微服務,就會出現大量的等待,如果還是持續地調用,則會造成大量請求的積壓,導致產品微服務最終也不可用

總結來說:如果一個服務不可用,而其他微服務還大量地調用這個不可用的微服務,也會導致其自身不可用,其自身不可用之後又可能繼續蔓延到其他與之相關的微服務上,這樣就會使得更多的微服務不可用,最終導致分佈式服務癱瘓

那麼怎麼防止這種情況的發生呢?

Hystrix-斷路器就是我們正確的選擇了,斷路器就如同電路中的保險絲,如果電器耗電大,導致電流過大,那麼保險絲就會熔斷,從而保證用電的安全。同樣地,在微服務系統之間大量調用可能導致服務消費者自身出現癱瘓的情況下,斷路器就會將這些積壓的大量請求“熔斷”,來保證其自身服務可用,而不會蔓延到其他微服務系統上。通過這樣的斷路機制可以保持各個微服務持續可用。

在Spring Cloud中斷路器是由NetFlix的Hystrix實現的,它默認監控微服務之間的調用超時時間爲2000 ms(2 s),如果超過這個超時時間,它就會根據你的配置使用其他方法進行響應。

我們做第一次實驗,還在在user節點中,修改之前能正常調用的getUserById方法
在這裏插入圖片描述
新增了這段方法,就是讓我們的程序休眠很長的時候以後在返回相應的結果值

 @GetMapping("/user/{id}")
    public User getUserById(@PathVariable("id")long id){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        User user=new User();
        user.setUserId(id);
        user.setUserName("user1");
        user.setUserPwd("admin");
        return user;
    }

再觀測product節點中相應的調用
在這裏插入圖片描述
我們這裏使用Feign訪問user節點下邊的 @GetMapping("/user/{id}")這個方法
在瀏覽器中訪問結果如下

我們看product後臺打印的錯誤信息
在這裏插入圖片描述
我們發現,並不是程序有錯誤(在沒加入睡眠代碼之前,這個程序是能正常運行的),發生的錯誤是Read timed out錯誤(訪問超時)

這裏沒做任何操作,是因爲ribbon有個默認的超時機制(好像是1.5S什麼的,記不清了),但是有時候我們的服務本身就需要容許一點超時,我們可以自己配置一下,在product節點的配置文件中添加如下配置

ribbon:
  ReadTimeout: 6000
  ConnectTimeout: 3000

(idea打死沒有提示,直接放上去就好了)
這樣我們配置了ReadTimeout: 6000爲6s,然後在user節點中是睡眠5s,再次測試,經歷很長時間等待之後(大概5s之後),我們得到了我們的需求結果
在這裏插入圖片描述
這樣可以避免了ribbon默認超時的困擾(特殊情況比如容許3s的情況也有)
我們繼續修改爲:

ribbon:
  ReadTimeout: 3000
  ConnectTimeout: 3000

超過3s就真的有點過分了,所以設置爲3s
然後在項目中引入我們的斷路器—Hystrix

第一步:引入pom文件

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

第二步:在你的spring cloud項目中啓用Hystrix
啓用的方式非常簡單,就是在你的main入口文件處加入這樣一句配置

@EnableCircuitBreaker

在這裏插入圖片描述
第三部:在需要降級服務的方法位置制定相應的規則
我們現在設置全局ribbon超時時間是3000ms,而Hystrix的默認超時時間是1000ms所以我們可以直接配置一下:
在這裏插入圖片描述
在getUser方法上邊標記位置設置

@HystrixCommand(fallbackMethod = “error”)

意思就是說,在斷路情況發生的時候,麻煩去找error方法處理斷路情況
代碼如下

 // 測試
    @GetMapping("product/getUser/{id}")
    @HystrixCommand(fallbackMethod = "error")
    public User getUser(@PathVariable int id) {
        User user = service.getUser(id);
        return user;
    }

PS:這裏請忽略返回值,一般情況下我們不會直接返回對象,而應該是封裝的response信息,裏邊包含data、status等信息,這裏爲了做實驗直接寫的返回user

然後我們這裏規定了斷路情況下去找error方法處理,所以我們先寫一個error方法

 /**
     * 降級服務調用方法
     * @return
     */
    public String error() {
        return "超時";
    }

然後重啓,運行服務,並且訪問該方法,然後結果報錯,錯誤信息如下:
在這裏插入圖片描述
這裏的錯誤原因是我們做實驗引發的必要錯誤,原因是我們的getUser這個方法,參數是public User getUser(@PathVariable int id) { 但是我們的error方法沒有任何參數,這裏需要兩邊保持一樣的參數,我們修改爲
在這裏插入圖片描述
然後再次執行,依舊報錯:
在這裏插入圖片描述
重點是這句話:

com.bigsoft.bigsoftproduct.controller.UserController.error(int)’ must
return: class com.bigsoft.bigsoftproduct.pojo.User or its subclass

也就是error方法的返回值也必須和getUser方法保持一直(再次說,一般返回值我們會統一封裝的,這裏做實驗隨意寫的返回值出錯了)
這裏強行讓返回值統一一下
在這裏插入圖片描述
這裏強行返回了和getUser方法一直的(爲了做實驗),再次執行
這裏我們user節點提供的服務睡眠了5000s,而Hystrix默認1000ms會啓用斷路,所以實驗結果如下
在這裏插入圖片描述
這裏返回到了error方法裏邊返回的空的User,證明實驗完成了。

這裏我們的Hystrix默認的斷路時間是1000ms,我們也可以對這個時間做修改,即認爲設置段榕時間(根據我們的服務器的實際工作性能來設置),設置方式有兩種,一種是註解的方式,註解方式如下:

 // 測試
    @GetMapping("product/getUser/{id}")
    @HystrixCommand(fallbackMethod = "error",
            commandProperties = { //設置屬性
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3200")
            })
    public User getUser(@PathVariable int id) {
        User user = service.getUser(id);
        return user;
    }

這裏我們在註解中設置了斷熔屬性,設置斷熔時間爲3200ms
第二種方式是配置的方式,代碼如下,在我們配置文件appliocation.yml文件中:

hystrix:
  command:
    default:
      execution:
        timeout:
          #如果enabled設置爲false,則請求超時交給ribbon控制,爲true,則超時作爲熔斷根據
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 5000 #斷路器超時時間,默認1000ms

這裏設置timeoutInMilliseconds的時間爲5000ms(當兩種方式同時設置的時候,註解方式有限)

特別注意:
這裏有個小坑,就是當你單獨使用Hystrix的時候上邊的代碼已經可以生效了,但是當我們fegin+Hystrix一起的時候,我們會發現上述代碼失效,原因就是這裏一共同時有兩個超時時間,一爲Hystrix的超時時間,二爲fegin的超時時間

那他們倆以哪個爲主主要是看上述文件中我們配置的

enabled: true

這個配置了,原則如下:

如果hystrix.command.default.execution.timeout.enabled爲true,則會有兩個執行方法超時的配置,一個就是ribbon的ReadTimeout,一個就是熔斷器hystrix的timeoutInMilliseconds, 此時誰的值小誰生效
如果hystrix.command.default.execution.timeout.enabled爲false,則熔斷器不進行超時熔斷,而是根據ribbon的ReadTimeout拋出的異常而熔斷,也就是取決於ribbon

所以我們配置的最終代碼如下:

ribbon:
  OkToRetryOnAllOperations: false #對所有操作請求都進行重試,默認false
  ReadTimeout: 3000   #負載均衡超時時間
  ConnectTimeout: 2000 #ribbon請求連接的超時時間
  MaxAutoRetries: 0     #對當前實例的重試次數,默認0
  MaxAutoRetriesNextServer: 1 #對切換實例的重試次數,默認1

hystrix:
  command:
    default:
      execution:
        timeout:
                   #如果enabled設置爲false,則請求超時交給ribbon控制,爲true,則超時作爲熔斷根據(兩個值一起生效,誰的值小誰生效)
          enabled: true

代碼中配置如下:

 // 測試
    @GetMapping("product/getUser/{id}")
    @HystrixCommand(fallbackMethod = "error",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3200")
            })
    public User getUser(@PathVariable int id) {
        User user = service.getUser(id);
        return user;
    }

在我們user節點代碼如下

  @GetMapping("/user/{id}")
    public User getUserById(@PathVariable("id")long id){
        try {
            Thread.sleep(2500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        User user=new User();
        user.setUserId(id);
        user.setUserName("user1");
        user.setUserPwd("admin");
        return user;
    }

現在我們的配置就是user節點休眠2500ms做出響應,而我們的projuct節點在3200ms的時候纔出發熔斷,我們來測試,根據剛纔的結論,我們現在說過當設置hystrix.command.default.execution.timeout.enabled爲true時候,ribbon的timneout和execution.isolation.thread.timeoutInMilliseconds這兩個值一起生效,而且誰的值小誰生效,這裏ribbon的值爲3000,理應ribbon的值生效
所以我們看實驗1:(ribbon3000,Hystrix3200,服務延時2500)
在這裏插入圖片描述
發現正常執行,然後我們進行實驗2:
在這裏插入圖片描述
設置Hystrix的時間爲2600ms
在這裏插入圖片描述
設置ribbon的超時時間爲2000ms
在這裏插入圖片描述
user節點延時時間爲2500ms
實驗結果如下:
在這裏插入圖片描述
我們發現被熔斷了,這符合我們上述的規律,當我們同時設置ribbon超時時間和Hystrix的超時時間,hystrix.command.default.execution.timeout.enabled爲true時候誰的超時時間短算誰的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章