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種執行命令的方法;
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方法返回的資訊。