1. 程式人生 > 實用技巧 >SpringCloud入門實戰(6)-Hystrix使用

SpringCloud入門實戰(6)-Hystrix使用

Hystrix是一個用於分散式系統的延遲和容錯的開源庫。在分散式系統裡,許多依賴不可避免的呼叫失敗,比如超時、異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導致整個服務失敗,避免級聯故障,以提高分散式系統的彈性。本文主要介紹Hystrix的基本使用,文中使用到的軟體版本:Spring Boot 2.2.5.RELEASE、Spring Cloud Hoxton.SR3、Java 1.8.0_191。

1、Hystrix作用

1.1、資源隔離

包括執行緒池隔離和訊號量隔離,限制呼叫分散式服務的資源使用,某一個呼叫的服務出現問題不會影響其他服務呼叫。

執行緒切換 支援非同步 支援超時 支援熔斷 限流 開銷
訊號量
執行緒池

1.2、降級

超時降級、資源不足時(執行緒或訊號量)降級,降級後可以配合降級介面返回託底資料。

1.3、融斷

當失敗率達到閥值自動觸發降級(如因網路故障/超時造成的失敗率高),熔斷器觸發的快速失敗會進行快速恢復。

1.4、快取

提供了請求快取、請求合併實現。

2、Hystrix處理過程

1.構造一個 HystrixCommand或HystrixObservableCommand物件,用於封裝請求,並在構造方法配置請求被執行需要的引數;
2.執行命令,Hystrix提供了4種執行命令的方法;

3.判斷是否使用快取響應請求,若啟用了快取,且快取可用,直接使用快取響應請求。Hystrix支援請求快取,但需要使用者自定義啟動;
4.判斷熔斷器是否開啟,如果開啟,跳到第8步;
5.判斷執行緒池/佇列/訊號量是否已滿,已滿則跳到第8步;
6.執行HystrixObservableCommand.construct()或HystrixCommand.run(),如果執行失敗或者超時,跳到第8步;否則,跳到第9步;
7.統計熔斷器監控指標;
8.走Fallback備用邏輯
9.返回請求響應

3、Hystrix引數說明

配置項預設值說明
hystrix.command.default.execution.isolation.strategy THREAD 隔離策略,THREAD, SEMAPHORE
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 1000 執行緒超時時間
hystrix.command.default.execution.timeout.enabled true 啟用超時設定
hystrix.command.default.execution.isolation.thread.interruptOnTimeout true 執行緒超時時,是否可被中斷
hystrix.command.default.execution.isolation.thread.interruptOnCancel false 執行緒取消時,是否可被中斷
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 10 最大併發訊號量
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests 10 最大併發備用訊號量
hystrix.command.default.fallback.enabled true 啟用fallback
hystrix.command.default.circuitBreaker.enabled true 啟用斷路器
hystrix.command.default.circuitBreaker.requestVolumeThreshold 20 在一個時間視窗內觸發斷路器的最小請求量
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds 5000 觸發斷路器時,服務休眠時間
hystrix.command.default.circuitBreaker.errorThresholdPercentage 50 觸發斷路器時,最小錯誤比率
hystrix.command.default.circuitBreaker.forceOpen false 強制開啟斷路器
hystrix.command.default.circuitBreaker.forceClosed false 強制關閉斷路器
hystrix.command.default.metrics.rollingStats.timeInMilliseconds 10000 資料採集時間段
hystrix.command.default.metrics.rollingStats.numBuckets 10 採集資料份,必須能被timeInMilliseconds整除
hystrix.command.default.metrics.rollingPercentile.enabled true 開啟採集滾動百分比
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds 60000 滾動百分收集時間段
hystrix.command.default.metrics.rollingPercentile.numBuckets 6 滾動百分比收集份數,必須能被timeInMilliseconds整除
hystrix.command.default.metrics.rollingPercentile.bucketSize 100 每份資料的最大統計請求量
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds 500 健康檢查間隔時間
hystrix.command.default.requestCache.enabled true 開啟請求快取,HystrixRequestCache
hystrix.command.default.requestLog.enabled true 開啟請求日誌,記錄在HystrixRequestLog
hystrix.collapser.default.maxRequestsInBatch Integer.MAX_VALUE 單批次最大請求量
hystrix.collapser.default.timerDelayInMilliseconds 10 批次請求延遲啟動時間
hystrix.collapser.default.requestCache.enabled true 開啟批次請求快取, HystrixCollapser.execute(), HystrixCollapser.queue()
hystrix.threadpool.default.coreSize 10 核心執行緒數
hystrix.threadpool.default.maximumSize 10 最大執行緒數
hystrix.threadpool.default.maxQueueSize -1 最大阻塞執行緒佇列
hystrix.threadpool.default.queueSizeRejectionThreshold 5 佇列上限,超過會拒絕請求
hystrix.threadpool.default.keepAliveTimeMinutes 1 執行緒保持活躍時間(分鐘)
hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize false 啟用maximumSize引數
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds 10000 執行緒池資料採集時間段
hystrix.threadpool.default.metrics.rollingStats.numBuckets 10 執行緒池資料採集份數

4、使用

4.1、結合springboot使用

4.1.1、引入依賴

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

4.1.2、配置Hystrix(application.yml)

根據需要配置Hystrix,也可以使用預設的配置。引數的意思可以參考上面的引數說明。

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000
      circuitBreaker:
        requestVolumeThreshold: 3

4.1.3、啟動類增加@EnableCircuitBreaker

package com.inspur.scdemo.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class ClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }
}

4.1.4、編寫controller

package com.inspur.scdemo.client.controller;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hystrix")
/**defaultFallback為預設的降級方法,沒有引數,返回值要與Hystrix方法的返回值相同*/
@DefaultProperties(commandProperties = {
    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "3"),
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000"),
}, defaultFallback = "defaulFallback")
public class HystrixController {
    private Logger logger = LoggerFactory.getLogger(HystrixController.class);

    @RequestMapping("/test")
    /**fallbackMethod為降級方法,需要與Hystrix方法的簽名和返回值一致*/
    @HystrixCommand(
        commandProperties = {
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "3"),
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
        },
        fallbackMethod = "fallback")
    public String test(long id) {
        logger.info(id + "");

        try {
            Thread.sleep(1000 * 5);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "test";
    }

    @RequestMapping("/test2")
    @HystrixCommand()
    public String test2(long id) {
        logger.info(id + "");

        try {
            Thread.sleep(1000 * 5);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "test2";
    }

    public String fallback(long id) {
        logger.info(id + "");
        return "fallback";
    }

    public String defaulFallback() {
        return "defaulFallback";
    }
}

方法上的配置優先順序最高,其次是類上的配置,最後時配置檔案中的配置。

訪問http://localhost:9002/hystrix/test?id=2,前三次會等待3秒,然後返回"fallback";第三次已觸發熔斷,第四次訪問會快速返回"fallback"

訪問http://localhost:9002/hystrix/test2?id=2,前三次會等待2秒,然後返回"defaultFallback";第三次已觸發熔斷,第四次會快速返回"defaultFallback"

4.2、與Feign結合使用

4.2.1、配置啟用Hystrix

feign:
  hystrix:
    enabled: true

4.2.2、定義Feign介面

package com.inspur.scdemo.client.feign;

import com.inspur.scdemo.client.entity.CallResult;
import com.inspur.scdemo.client.entity.User;
import com.inspur.scdemo.client.feign.fallback.UserFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

//scdemo-server為服務名稱
@FeignClient(name = "scdemo-server", fallback = UserFallBack.class)
public interface IUserFeign {
    @PostMapping("/user/addUser")
    CallResult<User> addUser(@RequestBody User user);

    @PostMapping("/user/getUser")
    CallResult<User> getUser(@RequestParam("id") long id);
}

4.2.3、定義降級實現類

package com.inspur.scdemo.client.feign.fallback;

import com.inspur.scdemo.client.entity.CallResult;
import com.inspur.scdemo.client.entity.User;
import com.inspur.scdemo.client.feign.IUserFeign;
import org.springframework.stereotype.Component;

@Component
public class UserFallBack implements IUserFeign {
    @Override
    public CallResult<User> addUser(User user) {
        return new CallResult<>(1, "增加使用者服務暫不可用!");
    }

    @Override
    public CallResult<User> getUser(long id) {
        return new CallResult<>(1, "獲取使用者服務暫不可用!");
    }
}

4.2.4、Controller中呼叫Feign介面

package com.inspur.scdemo.client.controller;

import com.inspur.scdemo.client.entity.CallResult;
import com.inspur.scdemo.client.entity.User;
import com.inspur.scdemo.client.feign.IUserFeign;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/userfeign")
public class FeignController {
    private Logger logger = LoggerFactory.getLogger(FeignController.class);
    @Autowired
    private IUserFeign userFeign;

    @RequestMapping("/addUser")
    public CallResult<User> addUser(String name, String address) {
        logger.info("name={},address={}", name, address);
        CallResult<User> result = new CallResult<>();
        try {
            User user = new User();
            user.setName(name);
            user.setAddress(address);
            CallResult<User> resultServer = userFeign.addUser(user);
            logger.info("server返回結果:{}" + resultServer);

            User user2 = resultServer.getResult();
            user2.setName("client-" + user2.getName());
            result.setResult(user2);
        } catch (Exception e) {
            result = new CallResult<User>(-1, "發生異常");
            e.printStackTrace();
        }
        return result;
    }

    @RequestMapping("/getUser")
    public CallResult<User> getUser(long id) {
        logger.info(id + "");

        CallResult<User> result = null;
        try {
            result = userFeign.getUser(id);
        } catch (Exception e) {
            result = new CallResult<User>(-1, "發生異常");
            e.printStackTrace();
        }
        return result;
    }
}

當scdemo-server服務不存在或呼叫超時,都會返回降級實現類中返回的資訊。

4.3、與SpringCloud Gateway結合使用

4.3.1、引入依賴

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

4.3.2、編寫回退controller

package com.inspur.scdemo.gateway.controller;

import com.inspur.scdemo.gateway.entity.CallResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FallbackController {
    @RequestMapping("/fallback")
    public CallResult<String> fallback() {
        return new CallResult<>(-10, "服務不可用");
    }
}

4.3.3、配置Hystrix filter

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false #是否開啟服務註冊和發現功能;將為每一個服務建立一個router,這個router把以服務名開頭的請求路徑轉發到對應的服務上
          lower-case-service-id: true #將請求路徑上的服務名轉為小寫
      routes:
      - id: client
        uri: lb://scdemo-client
        predicates:
        - Path=/scdemo-client/**
        filters:
        - StripPrefix=1 #路由時會去除/scdemo-client
        - name: Hystrix
          args:
            name: fallbackcmd
            fallbackUri: forward:/fallback

通過閘道器呼叫服務時,當scdemo-client服務不存在或呼叫超時,都會返回降級controller中fallback方法返回的資訊。