服务容错的背景
在微服务架构中,我们将业务拆分为一个个服务,服务于服务之间可以互相调用,但是由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量网络涌入,会形成任务堆积,最终导致服务瘫痪。
经典案例:服务雪崩
当单个实例出现故障时,导致上层调用其服务实例也变缓慢,导致请求堆积,负载升高,形同出现故障一般。进一步导致下一层掉用方也出现这种情况,最终导致整个架构出现服务器实例故障,形同雪崩,突然全线崩溃。这种由单个服务引发的级联故障称为服务雪崩。
雪崩发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方法相应变慢,亦或是某台机器的资源耗尽。我们无法完全杜绝雪崩效应源头的发生,只有做好足够的容错,保证一个服务发生问题,不影响到其他服务的正常运行,也就是雪落而不雪崩。
常见的服务容错模式
- 超时:最简单的容错方式,在调用方为服务调用设置一个超时时间,避免无限制的等待下游系统造成的线程堆积
- 限流:在提供方一端限制进入的流量,保证服务不会因为超负荷流量而崩溃。
- 仓壁模式(隔离):资源隔离手段。在调用方进行资源隔离,比如使用线程池,保证消耗的资源有上限,避免无限量调用资源占用其他请求的资源。
- 熔断器:在调用方进行控制,根据策略定义失败情况,失败数字达到阈值时打开熔断器,不再调用底层服务,熔断窗口(一段时间过后)达到时,释放一个请求进入底层,如果请求执行成功则关闭熔断器,正常调用,如果请求失败则继续保持熔断器打开直到下一个熔断窗口达到。熔断一般有三种状态:
- 熔断关闭状态:服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制。
- 熔断开启状态:后续对该服务接口的调用不再经过网络,直接执行本地的fallback方法。
- 半熔断状态:尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率,如果成功率达到预期,则说明服务已经恢复,进入熔断关闭状态。如果成功率很低,则重新进入熔断关闭状态。
常见容错组件
netflix-hystrix
- Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者是第三方库,防止级联失败,从而提升系统的可用性和容错性。
resillence4j
- Resilience4J是一款轻量级、简单、文档清晰、丰富的熔断工具,这也是Hystrix官方推荐的替代产品,不仅如此,Resilience4J还原生支持SpringBoot,而且监控也支持和promettheus等多款主流产品进行整合。
alibaba-sentinel
- Sentinel是阿里巴巴开源的一款断路器实现,本身在阿里内部已经被大规模采用,非常稳定。
sentinel的使用
- 添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<!--排除依赖,解决@RestController的类返回为xml格式的问题-->
<exclusions>
<exclusion>
<artifactId>jackson-dataformat-xml</artifactId>
<groupId>com.fasterxml.jackson.dataformat</groupId>
</exclusion>
</exclusions>
</dependency>
- 修改配置,暴露actutor的端点
# 应用名称
spring:
application:
name: sentineldemo
cloud:
nacos:
discovery:
server-addr: localhost:8848
server:
port: 8005
# actuator
management:
endpoints:
web:
exposure:
include: '*'
- 整合sentinel控制台,懒加载
下载jar包
启动命令如下
java -Dserver.port=8840 -Dcsp.sentinel.dashboard.server=localhost:8840 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.1.jar
访问 localhost:8840 用户名/密码:sentinel
- 修改应用配置,让应用连接上sentinel-dashboard
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: localhost:8840 # dashboard地址
client-ip: localhost # 本机地址,可以不填
- 在应用中开发一个端点 /hello,发请求给hello,在控制台刷新,看到sentineldemo项目的控制台界面控制台是懒加载的,需要先发请求,再能看到配置
@RestController
@Slf4j
public class HellowController {
@GetMapping("/hellow")
public Result hellowA(String name){
log.info("进入hellow");
return Result.success(name);
}
}