为什么需要集群流控呢?假设需要将某个API的总qps限制在100,机器数可能为50,这时很自然的想到使用一个专门的server来统计总的调用量,其他实例与该server通信来判断是否可以调用,这就是基本的集群流控方式,sentinel的实现就是这样的。

如果服务调用使用轮训或者随机路由方式,理论上可以通过在各个单机上设置流控规则即可(单机qps上限=总qps上限 / 机器数)。集群流控可以解决流量分配不均的问题导致总体流控效果不佳的问题,其可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果,不过由于会与server进行通信,所以性能上会有一定损耗。

集群流控中共有两种身份:

  • Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
  • Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。

Sentinel 1.4.0 开始引入了集群流控模块,主要包含以下几部分:

  • sentinel-cluster-common-default: 公共模块,包含公共接口和实体
  • sentinel-cluster-client-default: 默认集群流控 client 模块,使用 Netty 进行通信,提供接口方便序列化协议扩展
  • sentinel-cluster-server-default: 默认集群流控 server 模块,使用 Netty 进行通信,提供接口方便序列化协议扩展;同时提供扩展接口对接规则判断的具体实现(TokenService),默认实现是复用 sentinel-core 的相关逻辑

大致了解集群流控概念之后,下面一起分析下集群流控规则、client端和server端各自处理机制~

集群流控规则

FlowRule 添加了两个字段用于集群限流相关配置,如下所示。clusterMode在方法FlowRuleChecker.canPassCheck中会用到进行判断是否是集群流控,false表示单机流控;true表示集群流控,会调用方法passClusterCheck与集群流控server端通信判断是否触发了流控,此时异常降级策略为本地流控(fallbackToLocalOrPass方法,fallbackToLocalWhenFail属性为true时执行本地流控,否则直接返回ture不走流控检查)。

 private boolean clusterMode; // 标识是否为集群限流配置
private ClusterFlowConfig clusterConfig; // 集群限流相关配置项

// ClusterFlowConfig属性
private Long flowId; // 全局唯一的规则 ID,由集群限流管控端分配.
private int thresholdType = ClusterRuleConstant.FLOW_THRESHOLD_AVG_LOCAL; // 阈值模式,默认(0)为单机均摊,1 为全局阈值.
private int strategy = ClusterRuleConstant.FLOW_CLUSTER_STRATEGY_NORMAL;
private boolean fallbackToLocalWhenFail = true; // 在 client 连接失败或通信失败时,是否退化到本地的限流模式

public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount, boolean prioritized) {
String limitApp = rule.getLimitApp();
if (limitApp == null) {
return true;
}
if (rule.isClusterMode()) {// 集群模式
return passClusterCheck(rule, context, node, acquireCount, prioritized);
}
// 单机模式流控
return passLocalCheck(rule, context, node, acquireCount, prioritized);
}
  • flowId 代表全局唯一的规则 ID,Sentinel 集群限流服务端通过此 ID 来区分各个规则,因此务必保持全局唯一。一般 flowId 由统一的管控端进行分配,或写入至 DB 时生成。
  • thresholdType 代表集群限流阈值模式。单机均摊模式表示总qps阈值等于机器数*单机qps阈值;全局阈值等于整个集群配置的阈值。
  • strategy 集群策略,默认FLOW_CLUSTER_STRATEGY_NORMAL,针对ClusterFlowConfig配置该属性为FLOW_CLUSTER_STRATEGY_NORMAL才合法,除此之外,暂无太多业务意义。

client端处理机制

client端的处理机制和单机是一样的,只不过clusterMode和clusterConfig属性配置上了而已,具体的client使用可以参考官方文档 集群流控,这里不再赘述。如果是集群流控,在FlowRuleChecker.canPassCheck方法中会调用方法passClusterCheck,如下:

 private static boolean passClusterCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount,
boolean prioritized) {
try {
TokenService clusterService = pickClusterService();
if (clusterService == null) {
// 为null降级处理
return fallbackToLocalOrPass(rule, context, node, acquireCount, prioritized);
}
long flowId = rule.getClusterConfig().getFlowId();
TokenResult result = clusterService.requestToken(flowId, acquireCount, prioritized);
return applyTokenResult(result, rule, context, node, acquireCount, prioritized);
} catch (Throwable ex) {
RecordLog.warn("[FlowRuleChecker] Request cluster token unexpected failed", ex);
}
// 降级处理 本地限流
return fallbackToLocalOrPass(rule, context, node, acquireCount, prioritized);
}

requestToken负责与token server端通信,入参包括flowId, acquireCount, prioritized,这里是没有Resource信息的,server端通过flowid来获取对应规则进行流控判断。注意,调用writeAndFlush发送请求之后等待响应结果,最大等待时间ClusterClientConfigManager.getRequestTimeout();请求发送过程中,出现任何异常或者返回错误(这里不包括BLOCKED情况),都会默认走降级本地流控逻辑:fallbackToLocalOrPass

了解了client端处理流程,接下来看下server端处理流程,client和server端都是用netty作为底层网络通信服务,关于netty的原理不是本文讨论的重点因此会简单带过。如果小伙伴们还不太熟悉netty,请参阅对应资料即可。对于netty,每个Java开发者都需要了解甚至是熟悉的,这样不仅仅帮助我们理解NIO及Reactor模型,还能再阅读基于netty的框架源码(比如dubbo/rocketmq等)时,将重点关注在框架本身实现上,而不是网络通信流程及细节上。

server端处理机制

Sentinel 集群限流服务端有两种启动方式:

  • 独立模式(Alone),即作为独立的 token server 进程启动,独立部署,隔离性好,但是需要额外的部署操作。独立模式适合作为 Global Rate Limiter 给集群提供流控服务。

  • 嵌入模式(Embedded),即作为内置的 token server 与服务在同一进程中启动。在此模式下,集群中各个实例都是对等的,token server 和 client 可以随时进行转变,因此无需单独部署,灵活性比较好。但是隔离性不佳,需要限制 token server 的总 QPS,防止影响应用本身。嵌入模式适合某个应用集群内部的流控。

目前针对token server高可用,sentinel并没有对应的解决方案,不过没有并不意味着没考虑,因为默认可以降级走本地流控。sentinel作为一个限流组件,在大部分应用场景中,如果token server挂了降级为本地流控就可以满足了。

如果必须考虑token server高可用,可考虑token server集群部署,每个token server都能访问(或存储)全量规则数据,多个client通过特定路由规则分配到不同的token server(相同类型服务路由到同一个token server,不同类型服务可路由到不同token server),token server故障时提供failover机制即可。如果此时考虑到相同类型服务出现网络分区,也就是一部分服务可以正常与token server通信,另一个部分服务无法正常与token server通信,如果无法正常通信的这部分服务直接进行failover,会导致集群限流不准的问题,可通过zookeeper来保存在线的token server,如果zookeeper中token server列表有变化,再进行failover;此情况下再出现任何形式的网络分区,再执行降级逻辑,执行本地限流。

server端不管是独立模式还是嵌入模式,都是通过NettyTransportServer来启动的:

public void start() {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2));
p.addLast(new NettyRequestDecoder());
p.addLast(new LengthFieldPrepender(2));
p.addLast(new NettyResponseEncoder());
p.addLast(new TokenServerHandler(connectionPool));
}
});
b.bind(port).addListener(new GenericFutureListener<ChannelFuture>() {
//
});
}

以上逻辑主要是netty启动逻辑,重点关注initChannel方法,这些是往pipeline添加自定义channelHandler,主要是处理粘包、编解码器和业务处理Handler,这里最重要的是TokenServerHandler,因为是请求处理逻辑,所以重点关注其channelRead方法:

 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 全局保存channel
globalConnectionPool.refreshLastReadTime(ctx.channel());
if (msg instanceof ClusterRequest) {
ClusterRequest request = (ClusterRequest)msg;
if (request.getType() == ClusterConstants.MSG_TYPE_PING) {
// ping请求处理,会记录namespace信息
handlePingRequest(ctx, request);
return;
}
// 根据request type获取对应处理器
// 针对集群流控,type为MSG_TYPE_FLOW
RequestProcessor<?, ?> processor = RequestProcessorProvider.getProcessor(request.getType());
ClusterResponse<?> response = processor.processRequest(request);
writeResponse(ctx, response);
}
}

针对集群流控,type为MSG_TYPE_FLOW,对应处理器为FlowRequestProcessor。首先会提取请求入参 flowId, acquireCount, prioritized,主要步骤如下:

  • 根据flowId获取规则,为空返回结果NO_RULE_EXISTS;
  • 获取请求namespace对应的RequestLimiter,非空时进行tryPass限流检查,该检查是针对namespace维度;
  • 针对flowId对应规则进行限流检查,acquireCount表示该请求需要获取的token数,数据检查基于滑动时间窗口统计来判断的。

根据限流规则检查之后,会统计相关的PASS/BLOCK/PASS_REQUEST/BLOCK_REQUEST等信息,该流程和单机流控流程是类似的,具体代码不再赘述。处理完成之后,会返回client端处理结果,至此整个集群流控流程就分析完了。

往期精选

觉得文章不错,对你有所启发和帮助,希望能转发给更多的小伙伴。如果有问题,请关注下面公众号,发送问题给我,多谢。

欢迎小伙伴关注【TopCoder】阅读更多精彩好文。

sentinel 集群流控原理的更多相关文章

  1. Sentinel 发布里程碑版本,添加集群流控功能

    自去年10月底发布GA版本后,Sentinel在近期发布了另一个里程碑版本v1.4(最新的版本号是v1.4.1),加入了开发者关注的集群流控功能. 集群流控简介 为什么要使用集群流控呢?假设我们希望给 ...

  2. 快速体验 Sentinel 集群限流功能,只需简单几步

    ️ Pic by Alibaba Tech on Facebook 集群限流 可以限制某个资源调用在集群内的总 QPS,并且可以解决单机流量不均导致总的流控效果不佳的问题,是保障服务稳定性的利器. S ...

  3. Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之ORACLE集群概念和原理(二)

    ORACLE集群概念和原理(二) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体 ...

  4. redis sentinel 集群监控 配置

    环境: ip  172.16.1.31 26379  redis sentinel ip  172.16.1.30 6379   主 1 ip  172.16.1.31 6380   从 1 ip   ...

  5. Redis Sentinel集群双机房容灾实施步骤

    概要目标防止双机房情况下任一个机房完全无法提供服务时如何让Redis继续提供服务.架构设计A.B两机房,其中A机房有一Master一Slave和两个Sentinel,B机房只有2个Sentinel,如 ...

  6. redis sentinel集群的搭建

    背景说明: 这里采用1主2从的redis集群,3个sentinel搭建高可用redis集群. 一,关于搭建redis-sentinel高可用之前,我们必须要了解redis主从搭建redis-senti ...

  7. 『NiFi 节点本地流与集群流不一致导致集群加入失败』问题解决

    一.概述 在某些极端情况下,某些 NiFi 节点信息会由于用户强行 disconnect from cluster ,而出现 local flow 与 cluster 的 flow 不同步的问题. 此 ...

  8. 转载:【Oracle 集群】RAC知识图文详细教程(二)--Oracle 集群概念及原理

    文章导航 集群概念介绍(一) ORACLE集群概念和原理(二) RAC 工作原理和相关组件(三) 缓存融合技术(四) RAC 特殊问题和实战经验(五) ORACLE 11 G版本2 RAC在LINUX ...

  9. Redis Sentinel 集群安装 step by step

    一. 准备材料 服务器 IP address 操作系统 位数 Redis 版本   CNT06CAH05 192.168.3.47 CentOS 6.5 x64 Redis-3.2.6 sentine ...

随机推荐

  1. Spring boot参考指南

    介绍 转载自:https://www.gitbook.com/book/qbgbook/spring-boot-reference-guide-zh/details 带目录浏览地址:http://ww ...

  2. Cocos2d-X中国象棋的发展《五岁以下儿童》摆棋

    在博客上,以实现创建的游戏场景.而一些button,因为button落实到事件作出详细答复,需要使用一些功能摆棋.为此我特意button上的背面的具体实施, 在摆棋前先理清一下摆棋的思路: 1.创建一 ...

  3. tinkerpop(1) 地图数据库console科研

    原创文章连接: http://blog.csdn.net/freewebsys/article/details/46348975 转载请注明出处. 1,关于图数据库 tinkerpop是apache孵 ...

  4. Kafka Offset 1

    Kafka Offset Storage   1.概述 目前,Kafka 官网最新版[0.10.1.1],已默认将消费的 offset 迁入到了 Kafka 一个名为 __consumer_offse ...

  5. Extensible Access Control List Framework

    Methods, systems, and products for governing access to objects on a filesystem. In one general embod ...

  6. ArcGIS API for Silverlight 学习笔记

    这里主要讲解展示不同的服务地图 先看一个实例: 新建一个Silverlight项目,在MainPage.xaml文件中,引入 ESRI.ArcGIS.Client 命名空间和 ESRI.ArcGIS. ...

  7. 文章之间的基本总结Activity生命周期

    子曰:溫故而知新,能够為師矣.<論語> 学习技术也一样,对于技术文档或者经典的技术书籍来说,指望看一遍就全然掌握,那基本不大可能,所以我们须要常常回过头再细致研读几遍,以领悟到作者的思想精 ...

  8. HDU 3360 National Treasures 奇偶匹配的最低点覆盖

    标题来源:pid=3360">HDU 3360 National Treasures 意甲冠军:假设a[i][j] != -1 把他转成二进制 最多有12位 代表题目那张图的12个位置 ...

  9. 关于 IIS 上运行 ASP.NET Core 站点的“HTTP 错误 500.19”错误

    昨天回答了博问中的一个问题 —— “HTTP 错误 500.19 - Internal Server Error dotnetcore”,今天在这篇随笔中时候事后诸葛亮地小结一下. 服务器是 Wind ...

  10. WPF 4 动态覆盖图标(Dynamic Overlay Icon)

    原文:WPF 4 动态覆盖图标(Dynamic Overlay Icon)      在<WPF 4 开发Windows 7 任务栏>一文中我们学习了任务栏的相关开发内容,同时也对覆盖图标 ...