SpringCloud Sentinel

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

概述

Sentinel 功能

Sentinel 有着丰富的应用场景、完备的实时监控、广泛的开源生态以及完善的 SPI 扩展点。

在这里插入图片描述

Sentinel 的主要特性

在这里插入图片描述

Sentinel 组成

Sentinel 分为两个部分:

  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard):基于 Spring Boot 开发,打包后可以直接运行。

安装

参考 Docker Compose 一键部署

安装完之后,账号密码默认:Sentinel
在这里插入图片描述
因为暂时没有应用接入到 Sentinel 中,所以暂时没有什么内容。

基础

Sentinel 不算一个特别复杂的系统 ,别看架构图上 Sentinel 的周边是一系列的其它高大山的开源中间件,这不过是一种华丽的包装,其内核 Sentinel Core 确实是非常轻巧的。

限流

有单机和分布式两种:

单机限流是指限定当前进程里面的某个代码片段的 QPS(每秒查询次数) 或者 并发线程数 或者 整个机器负载指数,一旦超出规则配置的数值就会抛出异常或者返回 false。被限流的代码片段称为「临界区」。

分布式限流则需要一个集中的发票服务,这服务针对每个指定的资源每秒只会生成一定量的票数,在执行临界区的代码之前先去集中的发票服务领票,领成功了就可以执行,反之就会抛出限流异常。所以分布式限流代价较高,需要多一次网络读写操作。Sentinel 限流的原理和 Redis 的限流模块类似,Sentinel 的发票服务器使用了 Netty 框架。
在这里插入图片描述
Sentinel 在使用上提供了两种形式,一种是异常捕获形式,一种是布尔形式。也就是当限流被触发时,是抛出异常来还是返回一个 false

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;

public class SentinelTest {
	public static void main(String[] args) {
		// 配置规则
		List<FlowRule> rules = new ArrayList<>();
		FlowRule rule = new FlowRule();
		rule.setResource("tutorial");
		// QPS 不得超出 1
		rule.setCount(1);
		rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
		rule.setLimitApp("default");
		rules.add(rule);
        // 加载规则
		FlowRuleManager.loadRules(rules);
        // 下面开始运行被限流作用域保护的代码
		while (true) {
			Entry entry = null;
			try {
				// 限定了 tutorial 资源的单机每秒不能超过1次
				// 但是实际上运行是1秒2次,这多出来的执行逻辑就会被限制
				// 对应的 Sphu.entry() 方法就会抛出限流异常 BlockException。
				entry = SphU.entry("tutorial");
				System.out.println("hello world");
			} catch (BlockException e) {
				System.out.println("blocked");
			} finally {
				if (entry != null) {
					entry.exit();
				}
			}
			try {
				Thread.sleep(500); // 休息半秒
			} catch (InterruptedException e) {}
		}
	}
}

使用 Sentinel 需要指定限流规则,在规则的基础上,将临界区代码使用限流作用域结构包裹起来。

规则控制
上面的规则都是通过代码写死的,而实际使用规则需要支持动态配置。需要有一个 规则配置源,可以是 Redis、ZooKeeper 等,还需要有一个规则变更通知机制和规则配置控制台,允许管理人员可以在后台动态配置规则并实时下发到业务服务器进行控制。

在这里插入图片描述
有一些规则源存储不支持事件通知机制,比如关系数据库,Sentinel 也提供了定时刷新规则,比如每隔几秒来刷新内存里面的限流规则。

// redis 地址
RedisConnectionConfig redisConf = new RedisConnectionConfig("localhost", 6379, 1000);
// 反序列化算法
Converter<String, List<FlowRule>> converter = r -> JSON.parseArray(r, FlowRule.class);
// 定义规则源,包含全量和增量部分
// 全量是一个字符串key,增量是 pubsub channel key
ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<List<FlowRule>>(redisConf,"app_key", "app_pubsub_key", converter);
FlowRuleManager.register2Property(redisDataSource.getProperty());

健康状态上报与检查

接入 Sentinel 的服务要将自己的限流状态上报到 Dashboard,这样后台可以实时呈现所有服务的限流状态。Sentinel 使用 pull 模型来上报状态,它在当前进程注册了一个 HTTP 服务,Dashboard 会定时来访问这个 HTTP 服务来获取每个服务进程的健康状况和限流信息。
在这里插入图片描述
Sentinel 需要将服务的地址以心跳包的形式上报给 Dashboard,如此 Dashboard 才知道每个服务进程的 HTTP 健康服务的具体地址。如果进程下线了,心跳包就停止了,那么对应的地址信息也会过期,Dashboard 就能准实时知道当前的有效进程服务列表。

分布式限流

分布式限流需要另起一个 Ticket Server,由它来分发 Ticket,能够获取到 Ticket 的请求才可以允许执行临界区代码,Ticket 服务器也需要提供规则输入源。
在这里插入图片描述
Ticket Server 是单点的,如果 Ticket Server 挂掉了,应用服务器限流将自动退化为本地模式。

框架适配

Sentinel 保护的临界区是代码块,通过拓展临界区的边界就可以直接适配各种框架,比如 Dubbo、SpringBoot 、GRPC 和消息队列等。每一种框架的适配器会在请求边界处统一定义临界区作用域,用户就可以完全不必手工添加熔断保护性代码,在毫无感知的情况下就自动植入了限流保护功能。

熔断降级
限流在于限制流量,就是 QPS 或者线程的并发数,还有一种情况是请求处理不稳定或者服务损坏,导致请求处理时间过长或者老是频繁抛出异常,这时就需要对服务进行降级处理。所谓的降级处理和限流处理在形式上没有明显差异,也是以同样的形式定义一个临界区,区别是需要对抛出来的异常需要进行统计,这样才可以知道请求异常的频率,有了这个指标才会触发降级。

// 定义降级规则
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource("tutorial");
// 5s内异常不得超出10
rule.setCount(10);
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
rule.setLimitApp("default");
rules.add(rule);
DegradeRuleManager.loadRules(rules);
Entry entry = null;
try {
  entry = SphU.entry(key);
  // 业务代码在这里
} catch (Throwable t) {
  // 记录异常
  if (!BlockException.isBlockException(t)) {
    Tracer.trace(t);
  }
} finally {
  if (entry != null) {
    entry.exit();
  }
}

热点限流

还有一种特殊的动态限流规则,用于限制动态的热点资源。内部采用 LRU 算法计算出 topn 热点资源,然后对 topn 的资源进行限流,同时还提供特殊资源特殊对待的参数设置。 比如在限定了同一个用户的访问频次,同时也限定了同一本书的访问频次,但是对于某个特殊用户和某个特殊的书进行了特殊的频次设置。

在这里插入图片描述

ParamFlowRule ruleUser = new ParamFlowRule();
// 同样的 userId QPS 不得超过 10
ruleUser.setParamIdx(0).setCount(10);
// red用户特殊对待,QPS 上限是 100
ParamFlowItem uitem = new ParamFlowItem("red", 100, String.class);
ruleUser.setParamFlowItemList(Collections.singletonList(uitem));
ParamFlowRule ruleBook = new ParamFlowRule();
// 同样的 bookId QPS 不得超过 20
ruleBook.setParamIdx(1).setCount(20);
// redis 的书特殊对待,QPS 上限是 100
ParamFlowItem bitem = new ParamFlowItem("redis", 100, String.class);
ruleBook.setParamFlowItemList(Collections.singletonList(item));
// 加载规则
List<ParamFlowRule> rules = new ArrayList<>();
rules.add(ruleUser);
rules.add(ruleBook);
ParamFlowRuleManager.loadRules(rules)// userId的用户访问bookId的书
Entry entry = Sphu.entry(key, EntryType.IN, 1, userId, bookId);

热点限流的难点在于如何统计定长滑动窗口时间内的热点资源的访问量,Sentinel 设计了一个特别的数据结构叫 LeapArray,内部有较为复杂的算法设计。

系统自适应限流(过载保护)

当系统的负载较高时,为了避免被洪水般的请求冲垮,需要对当前的系统进行限流保护。保护的方式是逐步限制 QPS,观察到系统负载恢复后,再逐渐放开 QPS,如果系统的负载又下降了,就再逐步降低 QPS。如此达到一种动态的平衡,这涉及到一个特殊的保持平衡的算法。系统的负载指数存在一个问题,它取自操作系统负载的 load1 参数,load1 参数更新的实时性不足,从 load1 超标到恢复的过程存在一个较长的过渡时间,如果使用一刀切方案,在这段恢复时间内阻止任何请求,待 load1 恢复后又立即放开请求,势必会导致负载的大起大落,服务处理的时断时开。为此 Sentinel 将 TCP 拥塞控制算法的思想移植到这里实现了系统平滑的过载保护功能。这个算法很精巧,代码实现并不复杂,效果却是非常显著。

算法定义了一个稳态公式,稳态一旦打破,系统负载就会出现波动。算法的本质就是当稳态被打破时,通过持续调整相关参数来重新建立稳态。
在这里插入图片描述
稳态公式很简单:ThreadNum * (1/ResponseTime) = QPS,这个公式很好理解,就是系统的 QPS 等于线程数乘以单个线程每秒可以执行的请求数量。系统会实时采样统计所有临界区的 QPS 和 ResponseTime,就可以计算出相应的稳态并发线程数。当负载超标时,通过判定当前的线程数是否超出稳态线程数就可以明确是否需要拒绝当前的请求。

定义自适应限流规则需要提供多个参数:

  1. 系统的负载水平线,超过这个值时触发过载保护功能
  2. 当过载保护超标时,允许的最大线程数、最长响应时间和最大 QPS,可以不设置
List<SystemRule> rules = new ArrayList<SystemRule>();
SystemRule rule = new SystemRule();
rule.setHighestSystemLoad(3.0);
rule.setAvgRt(10);
rule.setQps(20);
rule.setMaxThread(10);
rules.add(rule);
SystemRuleManager.loadRules(Collections.singletonList(rule));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值