Spring Cloud 斷路器 Hystrix

在微服務架構中,一般存在多個服務調用層。微服務之間經過網絡進行通訊,從而支撐起整個應用,爲了保證高可用,單個服務一般也會集羣部署。但因爲網絡緣由或者自身緣由,服務並不能保證100% 可用。而服務間的依賴關係,會致使故障傳播,即服務提供者的不可用會致使消費者不可用,並把不可用逐漸放大,這就是雪崩效應。java

這裏就引入了請求超時機制和斷路器模式git

Hystrix 簡介

Hystrix 是 Netflix 的開源組件,是一個用於實現超時機制和斷路器模式的工具類庫。在微服務架構中,一般存在多個服務調用層。較低級別的服務中的服務故障可能致使級聯故障,一直到達用戶。當對特定服務的調用超過一個閾值(默認是 10秒/20個請求/故障百分比 > 50%),斷路器將打開而且不會進行調用。在出現錯誤和開路的狀況下,開發人員能夠提供一個回退github

斷路打開後能夠防止級聯故障。回退能夠是另外一個受 Hystrix 保護的調用、靜態數據或合理的空值。web

Ribbon 中整合 Hystrix

咱們在 Spring Cloud 服務消費(Ribbon) 中的例子基礎上添加 Hystrixspring

準備工做express

  • eureka-server 做爲服務註冊中心
  • product-service 做爲服務提供者
  • 複製工程 order-service-ribbon 爲 order-service-ribbon-hystrix 來整合 Hystrix

代碼參考:https://github.com/morgan412/spring-cloud-demo/tree/master/netflix網絡

添加 Hystrix 依賴架構

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

主類上添加註解 @EnableCircuitBreaker ,開啓斷路器功能app

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class OrderServiceRibbonHystrixApplication {

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

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

在須要容錯的方法上添加註解 @HystrixCommand,並指定回調(回退)方法ide

@Service
public class ProductService {
    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "getProductFallBack")
    public Product getProduct(Long id) {
        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);
    }

    public Product getProductFallBack(Long id) {
        Product product = new Product();
        product.setId(-1L);
        product.setName("默認商品");
        return product;
    }
}

@HystrixCommand 的配置能夠比較靈活,可使用 commandProperties 屬性列出 @HystrixProperty註釋,例如

@HystrixCommand(fallbackMethod = "getProductFallBack",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000")},
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "1"),
                    @HystrixProperty(name = "maxQueueSize", value = "10")
            })
@RestController
@Log4j2
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable Long id) {
        return productService.getProduct(id);
    }
}

驗證結果

  • 啓動項目 eureka-server、product-service、order-service-ribbon-hystrix
  • 訪問 http://localhost:8081/product/1 ,獲得正常結果
  • 關閉 product-service 服務,再訪問 http://localhost:8081/product/1 ,獲得結果 {"id":-1,"name":"默認商品","description":null,"price":null,"count":null}。說明當調用的服務不可用時,執行了回退方法

Feign 使用 Hystrix

Feign 使用聲明式的接口,沒有方法體,因此經過註解的方式顯然不適合 Feign。Spring Cloud 已經默認爲 Feign 整合了 Hystrix。咱們在 Spring Cloud 服務消費(Feign) 中的例子基礎上使用 Hystrix

準備工做

  • eureka-server 做爲服務註冊中心
  • product-service 做爲服務提供者
  • 複製工程 order-service-feign 爲 order-service-feign-hystrix 來使用 Hystrix

爲 Feign 添加回退

Spring Cloud 已經默認爲 Feign 整合了 Hystrix,因此咱們不須要添加依賴。配置文件中須要把 feign.hystrix.enabled 屬性設置爲 true

feign:
  hystrix:
    enabled: true

改寫 Feign 客戶端接口,在 @FeignClient 註解中經過 fallback 屬性指定回調類

@FeignClient(value = "product-service", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {

    @GetMapping("/product/{id}")
    Product getProduct(@PathVariable Long id);
}

建立回調類,這個類須要實現 @FeignClient 的接口,其中實現的方法就是對應接口的回退方法

@Component
public class ProductFeignClientFallback implements ProductFeignClient {
    @Override
    public Product getProduct(Long id) {
        Product product = new Product();
        product.setId(-1L);
        product.setName("默認商品");
        return product;
    }
}
@RestController
@Log4j2
public class ProductController {

    @Autowired
    private ProductFeignClient productFeignClient;

    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable Long id) {
        return productFeignClient.getProduct(id);
    }
}

驗證結果

  • 啓動項目 eureka-server、product-service、order-service-feign-hystrix
  • 訪問 http://localhost:8081/product/1 ,獲得正常結果
  • 關閉 product-service 服務,再訪問 http://localhost:8081/product/1 ,獲得結果 {"id":-1,"name":"默認商品","description":null,"price":null,"count":null}。說明當調用的服務不可用時,執行了回退方法

經過 fallbackFactory 檢查回退緣由

若是須要查看回退緣由,可使用註解 @FeignClient 中的 fallbackFactory 屬性

@FeignClient(value = "product-service", fallbackFactory = ProductClientFallbackFactoty.class)
public interface ProductFeignClient {

    @GetMapping("/product/{id}")
    Product getProduct(@PathVariable Long id);
}

同時建立 ProductClientFallbackFactoty 類,實現 FallbackFactory 接口,create 方法有個 Throwable 參數記錄回退緣由

@Component
@Log4j2
public class ProductClientFallbackFactoty implements FallbackFactory<ProductFeignClient> {
    @Override
    public ProductFeignClient create(Throwable throwable) {
        return new ProductFeignClient() {
            @Override
            public Product getProduct(Long id) {
                // 日誌最好放在各個 fallback 方法中,而不要直接放在 create 方法中
                // 不然在引用啓動時會打印該日誌
                log.info("fallback, caused by:", throwable);
                Product product = new Product();
                product.setId(-1L);
                product.setName("默認商品");
                return product;
            }
        };
    }
}

測試一下結果,當調用的服務不可用時,會進入回調方法中,並打印以下日誌

2019-05-06 14:21:20.384  INFO 8056 --- [ HystrixTimer-1] c.t.o.c.ProductClientFallbackFactoty     : fallback, caused by:
com.netflix.hystrix.exception.HystrixTimeoutException: null

Hystrix 監控

Hystrix 還提供了一些監控,當 HystrixCommand 和 HystrixObservableCommand 在執行時會產生會產生執行的結果和運行指標。能夠配合 spring-boot-starter-actuator 模塊同時設置 management.endpoints.web.exposure.include: hystrix.stream,經過 /actuator/hystrix.stream 端點來獲取指標信息

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream

當有 HystrixCommand 方法被執行,訪問 /actuator/hystrix.stream 能夠重複看到一些信息

Feign 項目須要添加依賴 spring-cloud-starter-netflix-hystrix,並在主類上加上註解 @EnableCircuitBreaker,才能使用 /actuator/hystrix.stream 端點

Hystrix Dashboard 監控面板

使用 Hystrix Dashboard 可讓上面的監控數據可視化,新建一個 Spring Boot 工程 hystrix-dashboard,添加依賴 spring-cloud-starter-netflix-hystrix-dashboard

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

而後在主類上添加註解 @EnableHystrixDashboard

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {

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

設置服務端口 server.port=8000,啓動 hystrix-dashboard,訪問 http://localhost:8000/hystrix 能夠看到如下界面

從上面的界面咱們能夠看到,Hystrix Dashboard 面板監控支持三種方式:默認的集羣監控、自定義的集羣監控、單個應用的監控。其中前兩個集羣的方式是須要結合 Turbine 實現,後面去用到。另外 Delay 是輪詢監控信息的延遲時間,Title 是監控界面標題,默認是監控實例的 URL

  • 默認的集羣監控:http://turbine-hostname:port/turbine.stream
  • 自定義的集羣監控: http://turbine-hostname:port/turbine.stream?cluster=[clusterName]
  • 單個應用監控: http://hystrix-app:port/actuator/hystrix.stream

那咱們把上面例子的 order-service-ribbon-hystrix 服務添加 hystrix 監控相關的端點,pom.xml 添加依賴 spring-boot-starter-actuator,設置配置屬性 management.endpoints.web.exposure.include=hystrix.stream,同時確保在主類上已經使用註解@EnableCircuitBreaker@EnableHystrix

完成配置後,啓動項目,把地址 http://localhost:8081/actuator/hystrix.stream 添加到面板頁面,能夠看到如下界面

上面展現了 getProduct 這個 HystrixCommand 的各項指標,各指標含義能夠看下圖

Turbine 聚合監控數據

Turbine 是聚合 Hystrix 監控數據的工具,上面的 Dashboard 面板能夠可視化一個 /actuator/hystrix.stream 端點的數據,可是對於多個服務和而且每一個服務有多個實例,來回的切換想要監控實例的地址就會很不方便。咱們須要一個把全部服務的 /actuator/hystrix.stream 端點數據整合起來的工具,這個工具就是 Turbine

建立一個 Spring Boot 項目命名爲 hystrix-turbine,部分的依賴入以下,該依賴默認包含了 Eureka Client

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

主類上添加註解 @EnableTurbine,該註解的配置中已經配置了服務註冊的功能,即至關於包含了 @EnableDiscoveryClient 的功能

@SpringBootApplication
@EnableTurbine
public class HystrixTurbineApplication {

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

配置信息以下

server:
  port: 8001
spring:
  application:
    name: hystrix-turbine
eureka:
  client:
    service-url:
      defaultZone:  http://localhost:8761/eureka/
      
turbine:
  app-config: order-service-ribbon-hystrix,order-service-feign-hystrix
  cluster-name-expression: "'default'"
  combine-host-port: true

turbine.app-config 配置用於查找實例的服務名
turbine.cluster-name-expression 指定集羣名稱,服務數量很的多時候,能夠啓動多個 Turbine 服務聚合不一樣的集羣數據,而該參數能夠用來區分不一樣的聚合
turbine.combine-host-port 經過主機名與端口號的組合來進行區分服務

咱們分別啓動項目 eureka-server:876一、product-service:807一、order-service-ribbon-hystrix:808九、order-service-feign-hystrix:8081

咱們能夠先測試下 http://localhost:8081/actuator/hystrix.streamhttp://localhost:8089/actuator/hystrix.stream 的端口有無問題,沒有問題咱們啓動 hystrix-dashboard,在頁面輸入 http://localhost:8001/turbine.stream 獲得的界面以下

相關文章
相關標籤/搜索