1 缘起
历旧项目,发现SpringBoot的版本为2.2.8.RELEASE,应该可以集成Hystrix,
实现熔断和降级测试,于是尝试在项目中集成,
可是查了相关资源,发现,很多资源都不完整,无法一步到位帮助开发者实现集成和测试,
所以,本文从实践到理论讲解Hystrix,帮助读者成功集成Hystrix并了解运作原理。
但是,没有讲为什么要使用Hystrix,这个读者可以自己总结。
第一,先从代码实践开始讲起,开发者阅读这一篇文章即可成功集成Hystrix到自己的项目中,
并给出测试样例和日志,有助于理解和验证结果。
第二,通过源码讲解Hystrix如何开启、读取属性,执行回调方法,并给出了Hystrix熔断和降级的流程示意图。
虽然,Hystrix已经进入维护阶段,但是,这不是开发者放弃学习的理由,
对于某些通用的功能和需求而言,只是使用不同的实现方式,不妨碍我们学习,
比如,新版的SpringBoot2.4.5及以后的版本,放弃Hystrix,使用Resllience4j,其实,也是熔断、降级和限流等功能,
Resilience4j的集成讲解参见:Chapter24:高可用组件resilience4j实现熔断、降级、限流
Resilience4j原理参见:详解SpringBoot服务限流原理之resilience4j
2 实践
该部分直接讲解如何集成Hystrix实现熔断和降级,
从架构的角度,讲解如何通过构建微服务,集成Hystrix,(完整的服务需要测试者自行搭建,这里讲解架构和具体的实验结果)
完成熔断和降级的测试和验证工作,
本文使用的微服务架构非常简单,如下图所示,
注册中心:Eureka;
被调用方:User服务;
调用方:Data服务(集成OpenFeign,调用User服务)。
版本:
SpringBoot 2.2.8.RELEASE
Eureka 2.2.3.RELEASE
Hystrix 2.2.8.RELEASE
OpenFeign 2.2.6.RELEASE
2.1 依赖
服务需要的依赖如下,核心有三个:Eureka、OpenFeign和Hystrix。
在Data服务(调用方)中集成Eureka、OpenFeign和Hystrix;
User服务中集成Eureka即可。
<!-- 服务注册和发现、客户端负载均衡、熔断 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
<!-- 服务间调用依赖Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
2.2 User服务
被Data服务调用的服务。
无特殊,正常开发接口(这里就不给样例了,可以任意发挥),
- 配置应用名称application.name=microservice-user;
- 添加Eureka注册中心;
2.2.1 配置
配置样例如下,集成Eureka,将User服务注册到Eureka。
spring:
profiles:
active: dev
application:
name: microservice-user
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8001/eureka/eureka # 与Eureka server(注册中心)交互地址,查询Eureka服务的地址
2.2.2 Controller
User服务对外提供的接口如下,这给了一个分页查询样例,
这是之前的旧项目,有数据库配置,直接就拿来用了,
如果开发者也想要测试Hystrix的熔断和降级,可以直接写一个简单的返回接口,
自定义响应延迟等。
package com.company.microserviceuser.controller;
import com.company.microserviceuser.exception.MyException;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import com.company.microserviceuser.dto.*;
import com.company.microserviceuser.enums.MyCodeEnums;
import com.company.microserviceuser.service.*;
import com.company.microserviceuser.vo.common.*;
import com.company.microserviceuser.vo.*;
import com.company.microserviceuser.dos.*;
import com.company.microserviceuser.utils.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static com.company.microserviceuser.constant.StringConstant.DATE_Y_M_D_H_M_S;
import com.company.microserviceuser.constant.*;
/**
* UserController API.
* @author xindaqi
* @since 2020-10-26
*/
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/v1/user")
@Api(tags = "人员信息")
public class UserController {
private static Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private TimeProcessUtil timeProcessUtil;
@Autowired
private IUserService userService;
@Autowired
private BCryptPasswordEncoder passwordEncoderCrypt;
@PostMapping(value = "/query/page")
@ApiOperation(value = "分页查询用户", notes = "分页查询用户v0.0", httpMethod = "POST")
@ApiImplicitParam(name = "params", value = "分页查询用户注册信息", dataType = "QueryUserByPageInputDTO", paramType = "body")
public ResponseVO<List<UserDetailsVO>> queryUserByPage(@RequestBody QueryUserByPageInputDTO params) {
try{
UserDO userDo = new UserDO();
Integer pageNum = params.getPageNum();
Integer pageSize = params.getPageSize();
BeanUtils.copyProperties(params, userDo);
PageInfo<UserDetailsVO> userInformation = userService.queryUserByPage(userDo, pageNum, pageSize);
List<UserDetailsVO> userList = userInformation.getList();
Long total = userInformation.getTotal();
logger.info("成功--分页查询用户");
return new ResponseVO<List<UserDetailsVO>>().ok(userList, total);
}catch (Exception e) {
logger.error("失败--列表分页查询用户:", e);
return new ResponseVO<List<UserDetailsVO>>().fail();
}
}
}
2.3 Data服务
- 使用OpenFeign调用User服务,需要在Data服务中集成OpenFeign;
- 熔断User服务需要集成Hystrix;
- 没有在启动类中使用启动Hystrix注解,如
@EnableHystrix、@EnableCircuitBreaker。
2.3.1 配置
Data服务添加:
直接添加如下配置,
- 注册到Eureka;
- 为Feign开启Hystrix;
- 同时设置Hystrix熔断配置;
- ribbon配置连接和响应时间上限。
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8001/eureka/eureka # 与Eureka server(注册中心)交互地址,查询Eureka服务的地址
hystrix:
command:
default:
circuitBreaker:
errorThresholdPercentage: 50 # 触发熔断的错误比例
sleepWindowMilliseconds: 100000 # 熔断后的休眠时间,单位:毫秒,即熔断开启到正常访问服务的时间间隔
requestVolumeThreshold: 2 # 触发熔断的最小请求次数
execution:
timeout:
enabled: true
isolation:
strategy: THREAD # 隔离策略:线程隔离
thread:
timeoutInMilliseconds: 10000 # 线程超时时间:5秒后,调用Fallback
ribbon:
ConnectTimeout: 2000 # 服务连接超时时间,单位:毫秒
ReadTimeout: 3000 # 获取响应超时时间,单位:毫秒,不可大于Hystrix超时时间
MaxAutoRetries: 0 # 最大自动重试次数
MaxAutoRetriesNextServer: 0 # 向集群其他服务最大重试次数
feign:
hystrix:
enabled: true #为Feign开启Hystrix
2.3.2 添加OpenFeign调用
Data服务使用OpenFeign调用User服务,
因此只需在Data服务中配置User服务的名称和URI即可完成调用,
为了使OpenFeign具有回调的实际内容(方法),需要在@FeignClient中配置fallback属性,
配置代码如下。
因为向注册中心Eureka注册,IP和PORT已在Eureka中互通,
Eureka相关参见:注册中心Eureka生命周期是怎样的
package com.company.microservicedata.feign;
import com.company.microservicedata.feign.impl.UserModuleFeignImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
import com.company.microservicedata.dto.*;
import com.company.microservicedata.vo.*;
import com.company.microservicedata.vo.common.*;
/**
* UserModule远程调用.
*
* @author xindaqi
* @since 2022-06-23 15:58
*/
@FeignClient(value="microservice-user", fallback = UserModuleFeignImpl.class)
public interface IUserModuleFeign {
/**
* 调用User模块的测试接口,验证Feign
*
* @param params 用户分页查询参数
* @return 用户详情-分页展示
*/
@RequestMapping("/api/v1/user/query/page")
ResponseVO<List<UserDetailsVO>> queryUserByPage(@RequestBody QueryUserByPageInputDTO params);
}
2.3.3 Hystrix熔断回调实现
上面配置了User服务的调用接口,
如果使用Hystrix进行回调,需要一比一实现上面的接口方法,
保证,各自方法有正确的响应类型映射,
样例如下:
package com.company.microservicedata.feign.impl;
import com.company.microservicedata.dto.QueryUserByPageInputDTO;
import com.company.microservicedata.feign.IUserModuleFeign;
import com.company.microservicedata.vo.UserDetailsVO;
import com.company.microservicedata.vo.common.ResponseVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* UserModule熔断回调实现.
*
* @author xindaqi
* @since 2022-06-23 15:58
*/
@Component
public class UserModuleFeignImpl implements IUserModuleFeign {
private static final Logger logger = LoggerFactory.getLogger(UserModuleFeignImpl.class);
@Override
public ResponseVO<List<UserDetailsVO>> queryUserByPage(QueryUserByPageInputDTO params) {
logger.info(">>>>>>>>>Hystrix feign fallback!");
return new ResponseVO<List<UserDetailsVO>>().ok(new ArrayList<>(), 0L);
}
}
2.3.4 Controller
通过Hystrix实现回调有两种方式,
一种:方法方式,直接实现OpenFeign的接口。
二种:注解方式,通过@HystrixCommand配置回调方法。
- 实现OpenFeign接口方式回调熔断方法
package com.company.microservicedata.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.beans.factory.annotation.Autowired;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import com.alibaba.fastjson.JSON;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.company.microservicedata.service.*;
import com.company.microservicedata.feign.*;
import com.company.microservicedata.dto.*;
import com.company.microservicedata.vo.*;
import com.company.microservicedata.vo.common.*;
import com.company.microservicedata.enums.*;
import com.company.microservicedata.util.ExcelProcessUtil;
import com.company.microservicedata.constant.StringConstant;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.company.microservicedata.util.TimeProcessUtil;
/**
* Feign call test.
* @author xindaqi
* @since 2020-12-02
*/
@CrossOrigin(origins="*", maxAge=3600)
@RestController
@RequestMapping("/v1/data")
@Api(tags = "User Feign")
public class UserInformationController {
private static Logger logger = LoggerFactory.getLogger(UserInformationController.class);
@Autowired
private IUserModuleFeign userModuleFeign;
@Autowired
private IUserInformationService userInformationService;
@Autowired
private ExcelProcessUtil excelProcessUtil;
@Autowired
private TimeProcessUtil timeProcessUtil;
@RequestMapping(value="/user/page", method=RequestMethod.POST)
@ApiOperation("分页查询用户信息")
public ResponseVO<List<UserDetailsVO>> queryUserByPage(@RequestBody QueryUserByPageInputDTO params) {
return userModuleFeign.queryUserByPage(params);
}
}
- 使用注解方式回调熔断方法
package com.company.microservicedata.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.beans.factory.annotation.Autowired;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import com.alibaba.fastjson.JSON;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.company.microservicedata.service.*;
import com.company.microservicedata.feign.*;
import com.company.microservicedata.dto.*;
import com.company.microservicedata.vo.*;
import com.company.microservicedata.vo.common.*;
import com.company.microservicedata.enums.*;
import com.company.microservicedata.util.ExcelProcessUtil;
import com.company.microservicedata.constant.StringConstant;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.company.microservicedata.util.TimeProcessUtil;
/**
* Feign call test.
* @author xindaqi
* @since 2020-12-02
*/
@CrossOrigin(origins="*", maxAge=3600)
@RestController
@RequestMapping("/v1/data")
@Api(tags = "User Feign")
public class UserInformationController {
private static Logger logger = LoggerFactory.getLogger(UserInformationController.class);
@Autowired
private IUserModuleFeign userModuleFeign;
@Autowired
private IUserInformationService userInformationService;
@Autowired
private ExcelProcessUtil excelProcessUtil;
@Autowired
private TimeProcessUtil timeProcessUtil;
@RequestMapping(value="/user/page", method=RequestMethod.POST)
@ApiOperation("分页查询用户信息")
@HystrixCommand(fallbackMethod = "com.company.microservicedata.feign.impl.UserModuleFeignImpl.queryUserByPage")
public ResponseVO<List<UserDetailsVO>> queryUserByPage(@RequestBody QueryUserByPageInputDTO params) {
return userModuleFeign.queryUserByPage(params);
}
public ResponseVO<List<UserDetailsVO>> queryUserByPageFallback(QueryUserByPageInputDTO params) {
logger.info(">>>>>>>>>Hystrix feign fallback!");
return new ResponseVO<List<UserDetailsVO>>().ok(new ArrayList<>(), 0L);
}
}
3 测试结果
3.1 User服务直接故障(DOWN)
这种情况,Data服务通过OpenFeign调用User服务,
由于为OpenFeign配置了Hystrix,此时,
Data服务会直接调用回调方法,返回默认数据。
当请求次数和失败率达到Hystrix配置后,Hystrix触发熔断,
在熔断等待期间,即使User服务重新可用,也不会将请求发送到User服务,
而是直接调用自身的回调方法,保证及时响应。
熔断及回调的时序如下图所示。
-
回调响应
Data调用结果如下图所示,User服务未启动,直接使用回调结果。
-
日志信息
Data服务运行,User服务未启动,此时,日志结果如下图所示,
由日志可知,进入回调方法,设定的信息:>>>>>>>>>Hystrix feign fallback!,
调用回调的线程为:hystrix-microservice-user-3,
由此可以推断,Hystrix是通过线程隔离的方式进行熔断。
-
熔断时间窗
在熔断时间窗内,即使User服务已经可用(启动),
Data服务仍不会将请求打到User服务,而是直接使用回调方法,
当过了熔断窗口,才会重新向User请求。
在熔断期内启动User服务,此时并没有请求进入,User日志信息如下图所示。
过了熔断期,User服务恢复正常,此时请求进入User服务,Data服务重新请求User服务,
两个服务的日志如下图所示,均有日志输出,并有traceId和spanId。
3.2 User延迟过高(高于Ribbon的最大响应时间)
Data服务未在设定的时间内获取User响应,
此时将User服务手动增加响应时延,如强制增加6秒的时延,
超过Ribbon的服务响应时间,如上面的配置:3秒,
此时,Data请求User服务,未在规定的时间内获得响应,直接调用回调方法,
但是,Data仍会向User发送请求,只是不等待其响应返回。
User接口的时延样例如下,仅增加了一行线程延时:Thread.sleep(6000)。
package com.company.microserviceuser.controller;
import com.company.microserviceuser.exception.MyException;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import com.company.microserviceuser.dto.*;
import com.company.microserviceuser.enums.MyCodeEnums;
import com.company.microserviceuser.service.*;
import com.company.microserviceuser.vo.common.*;
import com.company.microserviceuser.vo.*;
import com.company.microserviceuser.dos.*;
import com.company.microserviceuser.utils.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static com.company.microserviceuser.constant.StringConstant.DATE_Y_M_D_H_M_S;
import com.company.microserviceuser.constant.*;
/**
* UserController API.
* @author xindaqi
* @since 2020-10-26
*/
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/v1/user")
@Api(tags = "人员信息")
public class UserController {
private static Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private TimeProcessUtil timeProcessUtil;
@Autowired
private IUserService userService;
@Autowired
private BCryptPasswordEncoder passwordEncoderCrypt;
@PostMapping(value = "/query/page")
@ApiOperation(value = "分页查询用户", notes = "分页查询用户v0.0", httpMethod = "POST")
@ApiImplicitParam(name = "params", value = "分页查询用户注册信息", dataType = "QueryUserByPageInputDTO", paramType = "body")
public ResponseVO<List<UserDetailsVO>> queryUserByPage(@RequestBody QueryUserByPageInputDTO params) {
try{
// 强制延时6秒,超过Ribbon的响应时间
Thread.sleep(6000);
UserDO userDo = new UserDO();
Integer pageNum = params.getPageNum();
Integer pageSize = params.getPageSize();
BeanUtils.copyProperties(params, userDo);
PageInfo<UserDetailsVO> userInformation = userService.queryUserByPage(userDo, pageNum, pageSize);
List<UserDetailsVO> userList = userInformation.getList();
Long total = userInformation.getTotal();
logger.info("成功--分页查询用户");
return new ResponseVO<List<UserDetailsVO>>().ok(userList, total);
}catch (Exception e) {
logger.error("失败--列表分页查询用户:", e);
return new ResponseVO<List<UserDetailsVO>>().fail();
}
}
}
- 响应结果
User服务日志如下图所示,由此可知,Data的请求仍会向User发起。
Data服务,配置的Ribbon响应时间为3秒,
使用OpenFeign请求的服务,响应时间超过3秒,则不等待响应,
直接使用回调方法的结果,保证Data服务不会阻塞,避免级联灾难,
此时Data的响应日志如下图所示。
4 源码分析
4.1 OpenFeign启用Hystrix
如何为OpenFeign开启Hystrix,我开始也是苦恼,
开始在网上查到的很多不是通过配置文件开启Hystrix,
然后又再次搜索,终于查到使用配置文件为OpenFeign开启Hystrix,
可是,问题又来了,这个配置是如何生效的?谁读取的?
这不是通过@ConfigurationProperties通过前缀读取的,
而是@ConditionalOnProperty生效的,
源码如下图所示。
位置:org.springframework.cloud.openfeign.FeignClientsConfiguration.HystrixFeignConfiguration#feignHystrixBuilder
4.2 Hystrix配置
同样,我们配置了Hystrix属性之后,
这些属性是如何被读取到的?
当然,也不是常用的@ConfigurationProperties,
而是在程序中读取,
以hystrix为前缀,源码如下图所示。
位置:com.netflix.hystrix.HystrixCommandProperties
启动SpringBoot时会实例化HystrixPropertiesStrategyDefault,
继承Hystrix属性策略,获取填充的Hystrix属性,
源码如下图所示。
位置:com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategyDefault
接下来OpenFeign会配置相关的Hystrix属性,包括groupKey和commandKey,
为后面执行回调函数做准备,
源码如下图所示,
位置:feign.hystrix.SetterFactory
使用OpenFeign请求User服务时,
会配置相关属性,
Hystrix配置并不是在启动SprjngBoot时生效的,
而是在请求时生效,即通过OpenFeign请求服务时才会检查参数并载入参数。
上面步骤已经填充了Hystrix属性,
下面开始应用,第一步初始化构造器:HystrixCommand,
源码如下图所示:
位置:com.netflix.hystrix.HystrixCommand#HystrixCommand(com.netflix.hystrix.HystrixCommand.Setter)
通过继承HystrixCommandProperties填充属性,
源码如下图所示,因为在配置文件自定义了Hystrix属性,
所以会使用自定义的builder。
位置:com.netflix.hystrix.strategy.properties.HystrixPropertiesCommandDefault
如上所述,自定义的Hystrix属性,会进入自定义builder,
构造器:HystrixCommandProperties源码如下图所示,
位置:com.netflix.hystrix.HystrixCommandProperties#HystrixCommandProperties(com.netflix.hystrix.HystrixCommandKey, com.netflix.hystrix.HystrixCommandProperties.Setter)
这里讲一下如何读取配置文件值,源码如下图所示,
通过数据绑定后的前缀获取数据。
位置:com.netflix.hystrix.HystrixCommandProperties#getProperty(java.lang.String, com.netflix.hystrix.HystrixCommandKey, java.lang.String, java.lang.Boolean, java.lang.Boolean)
4.3 Ribbion
执行Hystrix相关的命令时,会进入Ribbon的切面,
首先是Ribbon的负载均衡,
位置:org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer
其次是填充Ribbon属性:
位置:org.springframework.cloud.netflix.ribbon.RibbonProperties
Ribbon默认属性:
位置:com.netflix.client.config.DefaultClientConfigImpl
- 如何读取ribbon的配置?
通过Environment读取yml配置,源码如下图所示,
位置:org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#getProperty
使用Environment的ribbion属性值配置Ribbon,源码如下图所示,
位置:org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonClientConfig
这些属性的初始化是在使用OpenFeign调用时才进行的,
SpringBoot启动时不会初始化Ribbon的相关配置(怎么知道的?断点调试,在【ribbonClientConfig内打断点】,以Debug方式启动SpringBoot,发现没有进入ribbonClientConfig断点,于是,请求,发现,进入断点),
由源码可知,如果没有自定义Ribbon参数,则会使用默认的Ribbon配置。
Ribbon配置的键名,源码如下图所示,
由源码可知,Ribbon绑定的属性名是大驼峰格式,
所以配置时需要按照这个约定,
位置:com.netflix.client.config.CommonClientConfigKey
4.4 执行回调
当使用OpenFeigin调用服务,触发熔断后,会使用Hystrix线程池调用fallback方法,
由上面的测试日志可知,调用fallback的线程:hystrix-microuser-n,默认n由10个,即1-10,可看源码找到Hystrix的ThreadPool。
回调有两种调用方式:同步回调和异步回调。
4.4.1 同步回调
同步回调使用execute,源码如下图所示,
这是HystrixCommand继承com.netflix.hystrix.HystrixExecutable而实现的方法,
由源码可知,HystrixCommand中调用了queue.get(),这其实是异步回调中的方法,
只是在同步回调中使用Future.get,所以是同步的,因为需要等待响应结果,
为什么是同步的,参考:Java基础系列:多线程的同步和异步
位置:com.netflix.hystrix.HystrixCommand#execute
4.4.2 异步回调
异步回调的源码如下图所示,
由源码可知,使用了Future作为返回类型,
由此可知,该方法使用了JUC并发编程,但是,获取结果的方式决定了同步和异步,
为什么是异步的,参考:Java基础系列:多线程的同步和异步
位置:com.netflix.hystrix.HystrixCommand#queue
4.5 熔断和降级流程
通过上面的分析,
总结一下Hystrix的熔断和降级的流程示意图,如下图所示。
5 小结
核心:
(1)Hystrix熔断和降级流程:自动装配相关Bean-》数据绑定-》OpenFeign请求服务-》HystrixCommanProperties填充数据-》HystrixCommand初始化-》HystrixExecutable.execute执行回调方法;
(2)Hystrix是通过线程隔离的方式进行熔断,执行回调由同步执行和异步执行两种方式;
(3)在熔断等待期间,即使被服务重新可用,也不会将请求发送到启动的服务,而是直接调用自身的回调方法,保证及时响应;
(4)被调用的服务过程:在线但超过Ribbon的响应时间,调用方即使使用了回调方法,但是请求仍然会到服务,只是不等待结果;
(5)为OpenFeign开启Hystrix:feign.hystrix.enabled=true;
(6)Hystrix结合Ribbon使用。