六:Spring Cloud 之服務降級與限流熔斷-Ribbon+Hystrix

1. 簡介

Netflix has created a library called Hystrix that implements the circuit breaker pattern. In a microservice architecture it is common to have multiple layers of service calls.
A service failure in the lower level of services can cause cascading failure all the way up to the user. When calls to a particular service reach a certain threshold (20 failures in 5 seconds is the default in Hystrix), the circuit opens and the call is not made. In cases of error and an open circuit a fallback can be provided by the developer.
Having an open circuit stops cascading failures and allows overwhelmed or failing services time to heal. The fallback can be another Hystrix protected call, static data or a sane empty value. Fallbacks may be chained so the first fallback makes some other business call which in turn falls back to static data.

  • Hystrix是netflix創建的一個用於服務降級、限流熔斷的組件
  • 微服務架構中一個服務調用鏈任何一個環節都有可能出現異常
  • 若對異常沒有合理的處理方式,則可能導致與此相關的功能不可用,甚至有可能引發雪崩現象
  • Hystrix可以很好的解決服務不可用問題,可採用直接返回錯誤狀態碼、靜態提示數據、以及轉調統一處理方法等方式對調用方及時做出相應

2. 代碼實現

2.1涉及的模塊

  • eureka-server-singleton:服務註冊中心,端口8761
  • eureka-service: 服務提供者,通過profile指定不同端口模擬一組微服務,端口8762、8763
  • eureka-service-ribbon-hystrix:通過Ribbon調用服務提供者提供的服務,並提供限流熔斷功能

2.2 源代碼

2.2.1 Github地址

https://github.com/andyChenHuaYing/spring-cloud-demo

2.2.2 切換

通過tag切換git tag -d v1.0,若想修改,可根據此tag創建新的分支。

Spring Cloud 之服務發現與調用-Ribbon#2.3 eureka-server-singleton 沒有任何區別

2.3 eureka-service

Spring Cloud 之服務發現與調用-Ribbon#2.4 eureka-service 沒有任何區別

2.4 eureka-service-ribbon-hystrix

2.4.1整體實現步驟

  1. pom.xml中引入spring-cloud-starter-netflix-eureka-serverspring-cloud-starter-netflix-ribbonspring-cloud-starter-netflix-hystrix依賴
  2. application.yml中指定配置項:端口、application name、eureka-server地址
  3. SpringBoot 啓動類添加註解@EnableHystrix開啓使用Hystrix功能
  4. 使用@Bean @LoadBalanced向Spring容器注入org.springframework.web.client.RestTemplate 實例
  5. 在使用之處使用@Autowired public RestTemplate restTemplate; 獲取實例,調用服務提供方提供的方法
  6. 在需要使用Hystrix服務降級、容錯功能的方法上使用@HystrixCommand(fallbackMethod = "hiError")指定回調方法

2.4.2 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-finchley-demo</artifactId>
        <groupId>org.oscar.scd</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-service-ribbon-hystrix</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    </dependencies>

</project>

2.4.2 application.yml

server:
  port: 8766

spring:
  application:
    name: eureka-service-ribbon-hystrix

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

2.4.3 EurekaServiceRibbonHystrixApplication

package org.oscar.scd.eureka.service.ribbon.hystrix;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableHystrix
@EnableEurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaServiceRibbonHystrixApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(EurekaServiceRibbonHystrixApplication.class, args);
    }
}

2.4.4 HelloRibbonHystrixController、HelloRibbonHystrixService

  • HelloRibbonHystrixService:
package org.oscar.scd.eureka.service.ribbon.hystrix.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class HelloRibbonHystrixService {

    @Autowired
    public RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "hiError")
    public String hiService(String name) {
        return restTemplate.getForObject("http://eureka-service//print?name=" + name, String.class);
    }

    public String hiError(String name) {
        return "Hi " + name + ", sorry, system error.";
    }

}
  • HelloRibbonHystrixController:
package org.oscar.scd.eureka.service.ribbon.hystrix.controller;

import org.oscar.scd.eureka.service.ribbon.hystrix.service.HelloRibbonHystrixService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/ribbon/hystrix")
public class HelloRibbonHystrixController {

    @Autowired
    public HelloRibbonHystrixService service;

    @GetMapping("/print")
    public String print(@RequestParam String name) {
        return this.service.hiService(name);
    }
}

3. 驗證

3.1 創建SpringBoot啓動類

3.1.1 EurekaServerSingletonApplication

最簡單的方式添加一個SpringBoot啓動類型的啓動類就行。

這裏寫圖片描述

3.1.2 EurekaServiceApplication-8762

EurekaServiceApplication-8762

3.1.3 EurekaServiceApplication-8763

與8762相同,只是修改 Active profiles 爲8763

3.1.4 EurekaServiceRibbonHystrixApplication

最簡單的方式添加一個SpringBoot啓動類型的啓動類就行。

3.2 啓動

  1. EurekaServerSingletonApplication
  2. EurekaServiceApplication-8762
  3. EurekaServiceApplication-8763
  4. EurekaServiceRibbonHystrixApplication

啓動完成後Run Dashboard界面:
這裏寫圖片描述

3.3 訪問服務信息界面

此時服務都是正常註冊狀態!

這裏寫圖片描述

3.4 服務提供者正常

  • 第一次訪問,正常返回,服務提供者端口爲:8763
    這裏寫圖片描述

  • 第二次訪問,也正常返回,服務提供者端口爲:8762
    這裏寫圖片描述

3.5 服務提供者異常

驗證Hystrix是否生效,整體步驟

1. 停止端口爲8762的服務提供者,觀察eureka-server信息界面,服務是否下線以及再次調用服務,觀察返回結果是否只有8763端口響應
2. 停止端口爲8763的服務提供者,觀察eureka-server信息界面,服務是否下線以及再次調用服務,觀察返回結果是否是Hystrix服務熔斷回調函數返回的結果
3. 若都是,則驗證Hystrix功能生效

3.5.1 停止端口8762服務

  1. eureka-server信息界面:停止的8762沒有像我們預期的下線,並且出現了紅色的提示(eureka-server進入了自我保護模式-見補充部分)
    這裏寫圖片描述

  2. 訪問服務:絕大部分時間返回的是8763,8762服務剛停時,偶爾會返回Hystrix熔斷回調函數的結果
    這裏寫圖片描述

3.5.2 停止端口8763服務

  1. eureka-server信息界面:
    這裏寫圖片描述
  2. 訪問服務:只會返回Hystrix熔斷回調函數的結果
    這裏寫圖片描述

到這裏,基本驗證了Hystrix啓作用了,但是這只是入門級別的使用,還有幾個疑點後續解決。

4. 思考

  • 服務下線之後,eureka-server信息界面爲什麼只出現紅色提示信息,並且沒有將服務下線?或者說是服務已下線但是沒有將服務狀態標識爲已下線或者不可用?
  • 這樣設計的理由是什麼?如何讓已停止提供服務的服務信息在eureka-server中看的到呢
  • 爲什麼剛停止兩個服務提供者之一時,訪問服務信息會偶爾出現Hystrix熔斷之後的回調函數處理結果?
  • Hystrix的處理方法能否指定使用單獨的Thread或者ThreadPool來執行,

5. 補充

5.1 資料

5.2 Eureka 自我保護

Eureka Server在運行期間,會統計心跳失敗的比例在15分鐘之內是否低於85%,如果出現低於的情況(在單機調試的時候很容易滿足,實際在生產環境上通常是由於網絡不穩定導致),Eureka Server會將當前的實例註冊信息保護起來,同時提示這個警告。保護模式主要用於一組客戶端和Eureka Server之間存在網絡分區場景下的保護。一旦進入保護模式,Eureka Server將會嘗試保護其服務註冊表中的信息,不再刪除服務註冊表中的數據(也就是不會註銷任何微服務)。
解決方式(或者說處理措施,在後續詳細使用或者問題整理中會解)

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