文章目录
1.概念
学习Hystrix之前,需要先了解几个概念
- 服务雪崩
- 服务熔断
- 服务降级
1.1 服务雪崩
在微服务的架构体系中,我们会将系统拆分成多个服务小单元,通过 HTTP 或者 RPC 进行远程调用。如下图所示:
在绝大多数情况下,服务消费者都能正常的远程调用服务提供者。但是某一时刻,服务提供者执行逻辑较慢,又或者网络出现抖动的情况,导致服务消费调用服务提供者超时或者失败。
比如当E服务出现异常后,这时候A服务和C服务分别调用了B服务和D服务,但是由于E服务异常了,最后连带了ABCDE都异常了,最后就造成了整体服务雪崩了
针对“服务雪崩”的情况,我们需要进行“服务容错”处理。解决的方向很“简单”,尽量不要去调用故障的服务,避免被拖垮。一般常用的手段有,主要是限流和开关。
-
限流
通过限制调用服务的频率,避免频繁调用故障服务,导致请求任务积压而自身雪崩。 -
开关
通过关闭对故障服务的调用,停止调用故障服务,从而避免服务雪崩。当然,关闭的前提是,不调用故障服务的情况下,业务逻辑依然可以走下去,或者业务数据的完整性不会被破坏。
一般来说,开关会分成手动开关和自动开关。手动开关比较好了解,自动开关是满足指定条件自动进行关闭。
1.2 服务降级
服务降级
这里有两种场景:
-
当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,增加响应速度!
-
当下游的服务因为某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速返回给用户!
其实应该要这么理解:
服务降级有很多种降级方式!如开关降级、限流降级、熔断降级!
服务熔断属于降级方式的一种!!!!!
其中服务熔断的含义为:
- 当下游的服务因为某种原因突然变得不可用或响应过慢,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。
以业内流行的Hystrix为例
Hystrix 内置断路器 HystrixCircuitBreaker 实现,一共有三种状态:
- CLOSED :关闭
- OPEN :打开
- HALF_OPEN :半开
其中,断路器处于 OPEN 状态时,链路处于非健康状态,命令执行时,直接调用回退逻辑,跳过正常逻辑,就不会去调用服务,而是直接降级
HystrixCircuitBreaker 状态变迁如下图 :
红线 :初始时,断路器处于 CLOSED 状态,链路处于健康状态。当满足如下条件,断路器从 CLOSED 变成 OPEN 状态:
- 在一个周期内,总请求数超过额定量,并其中的错误请求占总请求数超过一定比例 。
绿线 :断路器处于 OPEN 状态,命令执行时,若当前时间超过断路器开启时间一定时间,断路器变成 HALF_OPEN 状态,尝试调用正常逻辑,根据执行是否成功,打开或关闭熔断器【蓝线】。
配置
HystrixCommandProperties.default_metricsRollingStatisticalWindow = 10000 ms #请求周期
HystrixCommandProperties.circuitBreakerRequestVolumeThreshold = 20 #总请求数
HystrixCommandProperties.circuitBreakerErrorThresholdPercentage = 50% #错误请求比例
HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds = 5000ms #睡眠窗口时间
配置类截图HystrixCommandProperties
1.3 Hystrix资源隔离
Hystrix 使用了“舱壁隔离模式”来隔离和限制各个请求,从而实现资源的隔离。
Hystrix 通过线程池和信号量(Semaphore) 两种模式来实现隔离。
1.线程池模式
- 默认情况下,Hystrix 采用线程池模式来实现隔离。
- 针对调用的每一个服务,我们给其单独分配一个线程池。例如说,产品服务的调用分配在 A 线程池,用户服务的调用分配在 B 线程池。这样隔离后,两个服务的调用不会相互影响。
2.信号量模式
- 使用线程池模式来隔离时,需要进行上下文的切换,带来一定的性能损耗。因此,如果对性能有较高要求,且能够接受信号量模式不支持超时的情况,可以考虑采用信号量模式。
2.入门案例
案例是基于Nacos的服务,不详细贴出,可参考
https://blog.csdn.net/weixin_38650898/article/details/106796136
2.1 搭建一个服务提供者
- 控制器
@RestController
@RequestMapping("provider")
@Slf4j
public class ProviderController {
@Value("${server.port}")
private String port;
@GetMapping("/index/{name}")
public String testProvider(@PathVariable("name") String name) {
log.warn("testProvider: 服务提供者");
int i = 10 / 0; //为了测试hysrix断路器
return name + "端口号:" + port;
}
}
2.2 搭建一个服务消费者
- 依赖引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 控制器
@HystrixCommand
注解的参数:
fallbackMethod
:指定 fallback 服务降级的处理方法,处理相应的异常。ignoreExceptions
:指定忽略指定的异常 Class,不进行 fallback 服务降级。commandKey
:命令键,默认未配置情况下,使用方法名。groupKey
:命令分组键,用于 Hystrix 根据不同的分组来统计命令的统计、告警、仪表盘信息。默认未配置情况下,使用方法所在类名。threadPoolKey
:属性线程池名,用于划分不同的线程池,进行资源隔离。默认未配置情况下,相同 groupKey 的 Hystrix Command 使用同一个线程池。
@RestController
@RequestMapping("consumer")
@AllArgsConstructor
//@DefaultProperties(defaultFallback = "hystrixFallBack")
public class ConsumerController {
private final RestTemplate restTemplate;
@GetMapping("/hystrix/{name}")
@HystrixCommand(fallbackMethod = "hystrixFallBack")
public String hystrixConsumer(@PathVariable("name") String name) {
return restTemplate.getForObject("http://cloud-provider/provider/index/" + name, String.class);
}
/**
* 容错回调方法必须要含有调用服务的方法参数
*
* @param name
* @return
*/
public String hystrixFallBack(@PathVariable("name") String name, Throwable throwable) {
return "consumer 服务熔断,Reason:" + ExceptionUtils.getRootCauseMessage(throwable);
}
}
- 启动类
@EnableCircuitBreaker
:开启断路器
@EnableDiscoveryClient
@SpringBootApplication
@EnableCircuitBreaker
public class ConsumerApplication8082 {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication8082.class, args);
}
}
2.3 测试
由于服务提供者异常,会服务降级直接使用fallBack方法返回结果
疯狂点击后,满足熔断条件后就不会尝试调用服务,而是直接返回结果
3.Hystrix请求缓存
Hystrix
支持在同一个 HystrixRequestContext
上下文中,提供缓存的功能,以提升高并发场景下的性能,同时也带来相同缓存键返回相同结果的特性。
通过 Hystrix 主要提供了 @CacheResult 和 @CacheRemove 注解,我们可以使用 Hystrix 请求缓存功能。
@CacheResult
:添加在方法上,声明将方法的执行结果进行缓存,并后续从缓存中获取结果。
cacheKeyMethod
属性:设置缓存键的生成方法。
也可以使用 @CacheKey
注解,直接设置缓存键的字符串,它的优先级比 @CacheResult
的cacheKeyMethod
属性高。
@CacheRemove
:添加在方法上,声明移除指定 Hystrix Command 生成的缓存。
commandKey
属性:设置 Hystrix Command 键,一般是方法名。cacheKeyMethod
属性:设置缓存键的生成方法。
下面的项目都是基于上面的基础上修改的
3.1创建HystrixRequestContextFilter
/**
* @author :xiao7
* @date :Created in 2020/7/10 21:57
* @description:hystrix请求缓存过滤器
*/
@Component
@WebFilter(urlPatterns = "/")
public class HystrixRequestContextFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 初始化 HystrixRequestContext
HystrixRequestContext context = HystrixRequestContext.initializeContext();
// 继续过滤器
try {
chain.doFilter(request, response);
} finally {
// 销毁 HystrixRequestContext
context.close();
}
}
}
3.1创建请求缓存服务
@Service
@Slf4j
public class CacheService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "hystrixFallBack")
@CacheResult(cacheKeyMethod = "genCacheKey")
public String getProvider(String name) {
return restTemplate.getForObject("http://cloud-provider/provider/index/" + name, String.class);
}
@HystrixCommand
@CacheRemove(commandKey = "getProvider", cacheKeyMethod = "genCacheKey")
public void updateProvider(String name) {
log.info("[updateProvider][更新服务提供者({})详情]", name);
}
public String hystrixFallBack(@PathVariable("name") String name, Throwable throwable) {
return "consumer 服务熔断,Reason:" + ExceptionUtils.getRootCauseMessage(throwable);
}
public String genCacheKey(String name) {
return "PROVIDER_" + name;
}
}
3.3 新增控制层
一个请求多次调用同一个服务,同一个上下文请求中会调用缓存
@GetMapping("/getProvider/{name}")
public String getProvider(@PathVariable("name") String name) {
String a = cacheService.getProvider(name);
String b = cacheService.getProvider(name);
String c = cacheService.getProvider(name);
return c;
}
@GetMapping("/updateProvider/{name}")
public String updateProvider(@PathVariable("name") String name) {
String a = cacheService.getProvider(name);
cacheService.updateProvider(name);
String c = cacheService.getProvider(name);
return c;
}
3.4 测试
调用getProvider
接口,调用了三次服务提供者,只有第一次是真的调用,其他都是缓存
调用updateProvider
接口,update会清空请求缓存,重新调用
4.搭建Hystrix Dashboard 监控
修改Consumer消费者的配置文件
management:
endpoints:
web:
exposure:
include: "hystrix.stream" # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
新建项目,引入包
<!-- 实现对 Hystrix Dashboard 的自动配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
开启面板注解
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
5.相关文档
《Spring Cloud Netflix 官方文档 —— Hystrix Clients》
《Spring Cloud Netflix 官方文档 —— Hystrix Dashboard》
《Spring Cloud Netflix 中文文档 —— Hystrix Clients》
《Spring Cloud Netflix 中文文档 —— Hystrix 仪表板》