Spring Cloud Hystrix:服務容錯保護
SpringBoot實戰電商專案mall(20k+star)地址:
摘要
Spring Cloud Hystrix 是Spring Cloud Netflix 子專案的核心元件之一,具有服務容錯及執行緒隔離等一系列服務保護功能,本文將對其用法進行詳細介紹。
Hystrix 簡介
在微服務架構中,服務與服務之間透過遠端呼叫的方式進行通訊,一旦某個被呼叫的服務發生了故障,其依賴服務也會發生故障,此時就會發生故障的蔓延,最終導致系統癱瘓。Hystrix實現了斷路器模式,當某個服務發生故障時,透過斷路器的監控,給呼叫方返回一個錯誤響應,而不是長時間的等待,這樣就不會使得呼叫方由於長時間得不到響應而佔用執行緒,從而防止故障的蔓延。Hystrix具備服務降級、服務熔斷、執行緒隔離、請求快取、請求合併及服務監控等強大功能。
建立一個hystrix-service模組
這裡我們建立一個hystrix-service模組來演示hystrix的常用功能。
在pom.xml中新增相關依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
在application.yml進行配置
主要是配置了埠、註冊中心地址及user-service的呼叫路徑。
server:
port: 8401
spring:
application:
name: hystrix-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: https://localhost:8001/eureka/
service-url:
user-service: https://user-service
在啟動類上新增@EnableCircuitBreaker來開啟Hystrix的斷路器功能
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class HystrixServiceApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixServiceApplication.class, args);
}
建立UserHystrixController介面用於呼叫user-service服務
服務降級演示
- 在UserHystrixController中新增用於測試服務降級的介面:
@GetMapping("/testFallback/{id}")
public CommonResult testFallback(@PathVariable Long id) {
return userService.getUser(id);
}
- 在UserService中新增呼叫方法與服務降級方法,方法上需要新增@HystrixCommand註解:
@HystrixCommand(fallbackMethod = "getDefaultUser")
public CommonResult getUser(Long id) {
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
public CommonResult getDefaultUser(@PathVariable Long id) {
User defaultUser = new User(-1L, "defaultUser", "123456");
return new CommonResult<>(defaultUser);
}
- 啟動eureka-server、user-service、hystrix-service服務;
- 呼叫介面進行測試:
- 關閉user-service服務重新測試該介面,發現已經發生了服務降級:
@HystrixCommand詳解
@HystrixCommand中的常用引數
- fallbackMethod:指定服務降級處理方法;
- ignoreExceptions:忽略某些異常,不發生服務降級;
- commandKey:命令名稱,用於區分不同的命令;
- groupKey:分組名稱,Hystrix會根據不同的分組來統計命令的告警及儀表盤資訊;
- threadPoolKey:執行緒池名稱,用於劃分執行緒池。
設定命令、分組及執行緒池名稱
- 在UserHystrixController中新增測試介面:
@GetMapping("/testCommand/{id}")
public CommonResult testCommand(@PathVariable Long id) {
return userService.getUserCommand(id);
}
- 在UserService中新增方式實現功能:
@HystrixCommand(fallbackMethod = "getDefaultUser",
commandKey = "getUserCommand",
groupKey = "getUserGroup",
threadPoolKey = "getUserThreadPool")
public CommonResult getUserCommand(@PathVariable Long id) {
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
使用ignoreExceptions忽略某些異常降級
- 在UserHystrixController中新增測試介面:
@GetMapping("/testException/{id}")
public CommonResult testException(@PathVariable Long id) {
return userService.getUserException(id);
}
- 在UserService中新增實現方法,這裡忽略了NullPointerException,當id為1時丟擲IndexOutOfBoundsException,id為2時丟擲NullPointerException:
@HystrixCommand(fallbackMethod = "getDefaultUser2", ignoreExceptions = {NullPointerException.class})
public CommonResult getUserException(Long id) {
if (id == 1) {
throw new IndexOutOfBoundsException();
} else if (id == 2) {
throw new NullPointerException();
}
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
public CommonResult getDefaultUser2(@PathVariable Long id, Throwable e) {
LOGGER.error("getDefaultUser2 id:{},throwable class:{}", id, e.getClass());
User defaultUser = new User(-2L, "defaultUser2", "123456");
return new CommonResult<>(defaultUser);
}
- 呼叫介面進行測試:
- 呼叫介面進行測試:
Hystrix的請求快取
當系統併發量越來越大時,我們需要使用快取來最佳化系統,達到減輕併發請求執行緒數,提供響應速度的效果。
相關注解
- @CacheResult:開啟快取,預設所有引數作為快取的key,cacheKeyMethod可以透過返回String型別的方法指定key;
- @CacheKey:指定快取的key,可以指定引數或指定引數中的屬性值為快取key,cacheKeyMethod還可以透過返回String型別的方法指定;
- @CacheRemove:移除快取,需要指定commandKey。
測試使用快取
- 在UserHystrixController中新增使用快取的測試介面,直接呼叫三次getUserCache方法:
@GetMapping("/testCache/{id}")
public CommonResult testCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.getUserCache(id);
userService.getUserCache(id);
return new CommonResult("操作成功", 200);
}
- 在UserService中新增具有快取功能的getUserCache方法:
@CacheResult(cacheKeyMethod = "getCacheKey")
@HystrixCommand(fallbackMethod = "getDefaultUser", commandKey = "getUserCache")
public CommonResult getUserCache(Long id) {
LOGGER.info("getUserCache id:{}", id);
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
/**
* 為快取生成key的方法
*/
public String getCacheKey(Long id) {
return String.valueOf(id);
}
- 呼叫,這個介面中呼叫了三次getUserCache方法,但是隻列印了一次日誌,說明有兩次走的是快取:
測試移除快取
- 在UserHystrixController中新增移除快取的測試介面,呼叫一次removeCache方法:
@GetMapping("/testRemoveCache/{id}")
public CommonResult testRemoveCache(@PathVariable Long id) {
userService.getUserCache(id);
userService.removeCache(id);
userService.getUserCache(id);
return new CommonResult("操作成功", 200);
}
- 在UserService中新增具有移除快取功能的removeCache方法:
@CacheRemove(commandKey = "getUserCache", cacheKeyMethod = "getCacheKey")
@HystrixCommand
public CommonResult removeCache(Long id) {
LOGGER.info("removeCache id:{}", id);
return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id);
}
- 呼叫,可以發現有兩次查詢都走的是介面:
快取使用過程中的問題
- 在快取使用過程中,我們需要在每次使用快取的請求前後對HystrixRequestContext進行初始化和關閉,否則會出現如下異常:
java.lang.IllegalStateException: Request caching is not available. Maybe you need to initialize the HystrixRequestContext?
at com.netflix.hystrix.HystrixRequestCache.get(HystrixRequestCache.java:104) ~[hystrix-core-1.5.18.jar:1.5.18]
at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:478) ~[hystrix-core-1.5.18.jar:1.5.18]
at com.netflix.hystrix.AbstractCommand$7.call(AbstractCommand.java:454) ~[hystrix-core-1.5.18.jar:1.5.18]
- 這裡我們透過使用過濾器,在每個請求前後初始化和關閉HystrixRequestContext來解決該問題:
/**
* Created by macro on 2019/9/4.
*/
@Component
@WebFilter(urlPatterns = "/*",asyncSupported = true)
public class HystrixRequestContextFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
context.close();
}
}
}
請求合併
微服務系統中的服務間通訊,需要透過遠端呼叫來實現,隨著呼叫次數越來越多,佔用執行緒資源也會越來越多。Hystrix中提供了@HystrixCollapser用於合併請求,從而達到減少通訊消耗及執行緒數量的效果。
@HystrixCollapser的常用屬性
- batchMethod:用於設定請求合併的方法;
- collapserProperties:請求合併屬性,用於控制例項屬性,有很多;
- timerDelayInMilliseconds:collapserProperties中的屬性,用於控制每隔多少時間合併一次請求;
功能演示
- 在UserHystrixController中新增testCollapser方法,這裡我們先進行兩次服務呼叫,再間隔200ms以後進行第三次服務呼叫:
@GetMapping("/testCollapser")
public CommonResult testCollapser() throws ExecutionException, InterruptedException {
Future<User> future1 = userService.getUserFuture(1L);
Future<User> future2 = userService.getUserFuture(2L);
future1.get();
future2.get();
ThreadUtil.safeSleep(200);
Future<User> future3 = userService.getUserFuture(3L);
future3.get();
return new CommonResult("操作成功", 200);
}
- 使用@HystrixCollapser實現請求合併,所有對getUserFuture的的多次呼叫都會轉化為對getUserByIds的單次呼叫:
@HystrixCollapser(batchMethod = "getUserByIds",collapserProperties = {
@HystrixProperty(name = "timerDelayInMilliseconds", value = "100")
})
public Future<User> getUserFuture(Long id) {
return new AsyncResult<User>(){
@Override
public User invoke() {
CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
Map data = (Map) commonResult.getData();
User user = BeanUtil.mapToBean(data,User.class,true);
LOGGER.info("getUserById username:{}", user.getUsername());
return user;
}
};
}
@HystrixCommand
public List<User> getUserByIds(List<Long> ids) {
LOGGER.info("getUserByIds:{}", ids);
CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/getUserByIds?ids={1}", CommonResult.class, CollUtil.join(ids,","));
return (List<User>) commonResult.getData();
}
- 訪問,由於我們設定了100毫秒進行一次請求合併,前兩次被合併,最後一次自己單獨合併了。
Hystrix的常用配置
全域性配置
hystrix:
command: #用於控制HystrixCommand的行為
default:
execution:
isolation:
strategy: THREAD #控制HystrixCommand的隔離策略,THREAD->執行緒池隔離策略(預設),SEMAPHORE->訊號量隔離策略
thread:
timeoutInMilliseconds: 1000 #配置HystrixCommand執行的超時時間,執行超過該時間會進行服務降級處理
interruptOnTimeout: true #配置HystrixCommand執行超時的時候是否要中斷
interruptOnCancel: true #配置HystrixCommand執行被取消的時候是否要中斷
timeout:
enabled: true #配置HystrixCommand的執行是否啟用超時時間
semaphore:
maxConcurrentRequests: 10 #當使用訊號量隔離策略時,用來控制併發量的大小,超過該併發量的請求會被拒絕
fallback:
enabled: true #用於控制是否啟用服務降級
circuitBreaker: #用於控制HystrixCircuitBreaker的行為
enabled: true #用於控制斷路器是否跟蹤健康狀況以及熔斷請求
requestVolumeThreshold: 20 #超過該請求數的請求會被拒絕
forceOpen: false #強制開啟斷路器,拒絕所有請求
forceClosed: false #強制關閉斷路器,接收所有請求
requestCache:
enabled: true #用於控制是否開啟請求快取
collapser: #用於控制HystrixCollapser的執行行為
default:
maxRequestsInBatch: 100 #控制一次合併請求合併的最大請求數
timerDelayinMilliseconds: 10 #控制多少毫秒內的請求會被合併成一個
requestCache:
enabled: true #控制合併請求是否開啟快取
threadpool: #用於控制HystrixCommand執行所線上程池的行為
default:
coreSize: 10 #執行緒池的核心執行緒數
maximumSize: 10 #執行緒池的最大執行緒數,超過該執行緒數的請求會被拒絕
maxQueueSize: -1 #用於設定執行緒池的最大佇列大小,-1採用SynchronousQueue,其他正數採用LinkedBlockingQueue
queueSizeRejectionThreshold: 5 #用於設定執行緒池佇列的拒絕閥值,由於LinkedBlockingQueue不能動態改版大小,使用時需要用該引數來控制執行緒數
例項配置
例項配置只需要將全域性配置中的default換成與之對應的key即可。
hystrix:
command:
HystrixComandKey: #將default換成HystrixComrnandKey
execution:
isolation:
strategy: THREAD
collapser:
HystrixCollapserKey: #將default換成HystrixCollapserKey
maxRequestsInBatch: 100
threadpool:
HystrixThreadPoolKey: #將default換成HystrixThreadPoolKey
coreSize: 10
配置檔案中相關key的說明
- HystrixComandKey對應@HystrixCommand中的commandKey屬性;
- HystrixCollapserKey對應@HystrixCollapser註解中的collapserKey屬性;
- HystrixThreadPoolKey對應@HystrixCommand中的threadPoolKey屬性。
使用到的模組
springcloud-learning
├── eureka-server -- eureka註冊中心
├── user-service -- 提供User物件CRUD介面的服務
└── hystrix-service -- hystrix服務呼叫測試服務
專案原始碼地址
關於作者
macrozheng 【id:macrozheng】
專注Java技術分享,全套學習教程連載中,作者Github專案mall(20k+star)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/151/viewspace-2823843/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Spring Cloud Hystrix 服務容錯保護SpringCloud
- Spring Cloud Hystrix 容錯保護SpringCloud
- (十三)spring cloud微服務分散式雲架構-服務容錯保護(Hystrix斷路器)SpringCloud微服務分散式架構
- SpringCloud系列之服務容錯保護Netflix HystrixSpringGCCloud
- Spring Boot整合Hystrix實現服務容錯Spring Boot
- SpringCloud Alibaba實戰(9:Hystrix容錯保護)SpringGCCloud
- SpringCloud+Hystrix服務容錯SpringGCCloud
- Spring Cloud構建微服務架構-Hystrix服務降級SpringCloud微服務架構
- 微服務容錯限流框架Hystrix微服務框架
- spring cloud微服務架構-Eureka保護機制SpringCloud微服務架構
- Spring Cloud入門教程-Hystrix斷路器實現容錯和降級SpringCloud
- 7、Spring Cloud HystrixSpringCloud
- Spring Cloud OpenFeign:基於Ribbon和Hystrix的宣告式服務呼叫SpringCloud
- Spring Cloud 關於:Spring Cloud Netflix HystrixSpringCloud
- 微服務容錯限流Hystrix入門微服務
- Spring Cloud 之 Hystrix.SpringCloud
- Spring cloud(4)-熔斷(Hystrix)SpringCloud
- Spring Cloud Netflix—如何加入HystrixSpringCloud
- spring cloud 服務搭建(1)SpringCloud
- spring-cloud 服務治理SpringCloud
- spring cloud (一)服務治理SpringCloud
- 服務治理: Spring Cloud EurekaSpringCloud
- spring cloud微服務分散式雲架構--hystrix的使用SpringCloud微服務分散式架構
- 服務容錯模式模式
- Spring Cloud Hystrix應用篇(十一)SpringCloud
- Spring Cloud Hystrix原碼篇(十一)SpringCloud
- Spring Cloud Eureka原理分析(二):續租、下線、自我保護機制和自動清理(服務端)SpringCloud服務端
- Spring Cloud構建微服務架構-spring cloud服務監控中心SpringCloud微服務架構
- Spring Cloud Kubernetes服務發現SpringCloud
- 《springcloud 四》服務保護機制SpringGCCloud
- 什麼是重保服務?重保服務工作內容有哪些?
- spring cloud微服務分散式雲架構(四)-斷路器(Hystrix)SpringCloud微服務分散式架構
- 服務治理->搭建服務註冊中心: Spring Cloud EurSpringCloud
- Spring Cloud Spring Boot mybatis分散式微服務雲架構-hystrix引數詳解CloudSpring BootMyBatis分散式微服務架構
- Spring Cloud Feign 宣告式服務呼叫SpringCloud
- Spring Cloud服務發現元件EurekaSpringCloud元件
- Spring Cloud Alibaba(8)---Feign服務呼叫SpringCloud
- 宣告式服務呼叫 Spring Cloud FeignSpringCloud