想要学习完整SpringCloud架构可跳转:SpringCloud Alibaba微服务分布式架构
分布式系统的流量防卫兵
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。
Sentinel以流星为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
丰富的应用场景: Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控: Sentinel同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群的汇总运行情况。
广泛的开源生态: Sentinel提供开箱即用的与其它开源框架/库的整合模块,例如与Spring Cloud、Dubbo、gRPC的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
完善的SPI扩展点: Sentinel提供简单易用、完善的SPI扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Hystrix | Sentinel |
---|---|
需要手工搭建监控平台:DashBoard | 单独一个组件独立出来 |
没有一套Web界面可以进行更加颗粒化的配置流量监控、速率控制、服务熔断、服务降级 | 支持界面化的细粒度统一配置 |
核心库(Java客户端)不依赖任何框架/库,能够运行于所有Java运行时环境,同时对 Dubbo /Spring Cloud 等框架也有较好的支持。
控制台(Dashboard)基于Spring Boot 开发,打包后可以直接运行,不需要额外的Tomcat等应用容器。
启动命令:
java -jar sentinel-dashboard-1.7.1.jar
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
server:
port: 8401
servlet:
context-path: /sentinel
spring:
# 服务名称
application:
# 订单服务
name: nacos-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos 服务注册中心地址
namespace: 13e20987-6fcc-41d6-a358-e16b86bce522
config:
server-addr: localhost:8848 # Nacos 配置中心地址
file-extension: yaml # 指定yaml格式的配置
group: DEV_GROUP
namespace: 13e20987-6fcc-41d6-a358-e16b86bce522
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,加入被占用会自动从8719开始依次+1扫描,知道找到未被占用的端口
port: 8719
management:
endpoints:
web:
exposure:
include: "*"
资源名: 唯一名称,默认请求路径
针对来源: Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
阈值类型/单机阈值:
是否集群: 不需要集群
流控模式:
流控效果:
Warm Up ( RuleConstant.CONTROL_BEHAVIOR_MARM_UuP)方式,即预热/冷启动方式。
当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
默认 coldFactor为3,即请求QPS从threshold / 3 开始,经预热时长逐渐升致设定的QPS阈值。
匀速排队( RuleConstant.CONTROL_BEHAVIOR_RATE_LINITER )方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
用于处理间隔性突发的流量,例如消息队列。
在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException )。
Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错谒。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException) 。
平均响应时间(DEGRADE GRADE_RT ):当1s内持续进入5个请求,对应时刻的平均响应时间(秒级)均超过阈值( count,以ms为单位),那么在接下的时间窗口( DegradeRule中的timewindow,以s 为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException )。注意Sentinel默认统计的RT上限是4900ms,超出此阈值的都会算作4900ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置。
每秒钟进来10个线程,我们Sentinel上配置的是希望200毫秒处理完本次任务,如果没有处理完,在未来1秒钟的时间窗口内,断路器打开,微服务不可用。
QPS >= 5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
异常比例( DEGRADE_GRADE_EXCEPTION_RATIO ):当资源的每秒请求量>= 5,并且每秒异常总数占通过量的比值超过阈值( DegradeRule中的 count )之后,资源进入降级状态,即在接下的时间窗口( DegradeRule中的 timewindow,以s为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0,1.0],代表0%- 100%。
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
异常数( DE6RADE_GRADE_EXCEPTION_COUNT ):当资源近1分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timewindow小于60s,则结束熔断状态后仍可能再进入熔断状态。
时间窗口一定要大于等于60S。
半开状态:系统自动会去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。(Hystrix)
注意:异常降级仅针对业务异常,对Sentinel限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过Tracer.trace(ex)记录业务异常。
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的Top K数据,并对其访问进行限制。
比如:
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel利用LRU策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
源码:
Entry entry = null;
try {
entry = SphU.entry(resourceName,EntryType.IN,1,paramA,paramB);
// Your logic here.
}catch (BlockException ex) {
//Handle request rejection.
}finally{
if (entry != null) {
entry.exit(1,paramA,paramB);
}
}
测试案例:
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false)String p1,
@RequestParam(value = "p2",required = false)String p2){
return "----testHotKey";
}
//兜底方法
public String deal_testHotKey(String p1, String p2, BlockException exception){
//Sentinel系统默认提示:Blocked by Sentinel (flow limiting)
return "deal_testHotKey,o(╥﹏╥)o";
}
@SentinelResource(value = “testHotKey”,blockHandler = “deal_testHotKey”)
第一个参数只要QPS超过每秒1次,马上降级处理,用自定义的deal_testHotKey兜底方法
@SentinelResource
处理的是Sentinel控制台配置的违规情况,有blockHandler 方法配置的兜底处理
主管配置出错,运行出错该走异常走异常
Sentinel系统自适应限流从整体维度对应更入口流量进行控制,结合应用的Load、CPU使用率、总体平均RT、入口QPS和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统规则支持以下的模式:
自定义异常兜底处理类
public class CustomerBlockFallback {
public static CommonResult handlerFallback(@PathVariable("id") Long id,Throwable e){
return CommonResult.BlockHandler("兜底异常--Fallback,exception内容:"+e.getMessage());
}
}
自定义Sentinel控制台配置违规处理类
public class CustomerBlockHandler {
public static CommonResult handlerblock(@PathVariable("id") Long id, BlockException exception) {
return CommonResult.BlockHandler(exception.getMessage());
}
}
业务层
@Service
public class PaymentServiceImpl {
@Resource
private PaymentService paymentService;
@SentinelResource(value = "getPaymentById",
fallbackClass = CustomerBlockFallback.class,
fallback = "handlerFallback", //fallback只负责业务异常
blockHandlerClass = CustomerBlockHandler.class,
blockHandler = "handlerblock", //只负责Sentinel控制台配置违规
exceptionsToIgnore = {IllegalArgumentException.class} //排除该异常的兜底方法
)
public CommonResult<Payment> getPaymentById(Long id) {
if (id<0){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常");
}
CommonResult<Payment> paymentById = paymentService.getPaymentById(id);
if (paymentById.getData()==null){
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return paymentById;
}
}
Sentinel | Hystrix | resilience4j | |
---|---|---|---|
隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔离/信号量隔离 | 信号量隔离 |
熔断降级策略 | 基于响应时间、异常比例、异常数 | 基于异常比例 | 基于异常比例、响应时间 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于RxJava) | Ring Bit Buffer |
动态规则配置 | 支持多种数据源 | 支持多种数据源 | 有限支持 |
扩展性 | 多个扩展点 | 插件的形式 | 接口的形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
限流 | 基于QPS,支持基于调用关系的限流 | 有限的支持 | Rate Limiter |
解决方案:保存进Nacos(官方要求)、Mysql、Redis
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
11.13.2 配置文件
spring:
# 服务名称
application:
# 订单服务
name: nacos-payment-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos 服务注册中心地址
namespace: 13e20987-6fcc-41d6-a358-e16b86bce522
config:
server-addr: localhost:8848 # Nacos 配置中心地址
file-extension: yaml # 指定yaml格式的配置
group: DEV_GROUP
namespace: 13e20987-6fcc-41d6-a358-e16b86bce522
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,加入被占用会自动从8719开始依次+1扫描,知道找到未被占用的端口
port: 8719
datasource:
dsl:
nacos:
server-addr: localhost:8848
namespace: 13e20987-6fcc-41d6-a358-e16b86bce522
dataId: nacos-payment-consumer
groupId: DEV_GROUP
data-type: json
rule-type: flow
resource: 资源名称;
limitApp: 来源应用;
grade: 阈值类型,0表示线程数,1表示QPS;
count: 单机阈值;
strategy: 流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior: 流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode: 是否集群。