Sentinle集群流控【源码笔记】
前言
本文从集群流控概览入手,按照概览的步骤逐步分析各个步骤的源码实现过程。由于集群流控中token server可以是独立部署也可以是使用内嵌模式(即集群应用中的其中一个作为token server)。本文主要以内嵌模式为主。
一、集群流控概览
1.集群流控入口
FlowSlot#checkFlow入口,由FlowRule.clusterMode来设置是否为集群流控,默认false。
2.集群流控流程
说明
@1 TokenService初始化。通过SPI方式获取TokenService,client位于模块sentinel-cluster-client-default的META-INF,默认为DefaultClusterTokenClient;server位于sentinel-cluster-server-default模块的META-INF,默认为DefaultEmbeddedTokenServer;0表示client,1表示server
@2 降级为单机流控。在没有找到TokenService时,降级为单机流控由ClusterFlowConfig参数fallbackToLocalWhenFail决定,默认true
@3 流控规则ID,全局唯一标识
@4 client与server交互过程。client向server请求token,server端处理client请求,使用netty通信
@5 处理返回结果TokenResult
@6 集群流控失败,默认降级为单机流控
类图
二、TokenService初始化
TokenService初始化的初始化由client/server两部分构成。
1.DefaultClusterTokenClient初始化
@1 注册server变更观察者,在server变更时回调changeServer方法,此处可以借鉴如何使用观察者模式。
调用时机 下面以示例中给出的例子为入口切入,观察server变更时的回调流程。
示例中使用动态数据源,在配置变更后执行getProperty().updateValue(newValue)让规则生效。
ClusterClientConfigManager通过静态方法clientAssignProperty注册ClientAssignPropertyListener,监听ClusterClientAssignConfig的变更,主要是server地址和端口的变更。
ClusterClientAssignConfig配置
serverHost:server地址
serverPort:server端口
clientConfigProperty注册ClientConfigPropertyListener监听ClusterClientConfig的变更,主要是超时时间的变更。
ClusterClientConfig配置
requestTimeout:请求超时时间
重新连接server
小结:client初始化时注册观察者,在server配置(server地址与端口)变更时通过netty重新连接到新的server地址。
@2 初始化新连接,此处并未真正与server建立连接
说明:通过ClusterStatePropertyListener进行状态变更时触发与server的连接;ClusterStatePropertyListener在ClusterStateManager初始化时被添加到stateProperty,主要负责client、server角色的切换。
下面示例demo中,通过监听动态配置,来动态切换client与server,判断逻辑见下图备注。
小结:监听client/server的动态配置,ClusterStatePropertyListener负责client/server的状态切换,即:如果本节点是server被变更为client,则先关闭server在启动client;反之同理。0表示client,1表示server,-1表示未启动。
2.DefaultEmbeddedTokenServer初始化
DefaultEmbeddedTokenServer启动时使用SentinelDefaultTokenServer。
@1 注册观察者,通过动态数据源(zk、nacus等)监听serve端通信配置ServerTransportConfig配置的变更,有变更时重新启动server。
ServerTransportConfig配置信息
port:端口号
idleSeconds:连接空闲时间,默认为600秒,超过会关闭连接
更新ServerTransportConfig生效由ClusterServerConfigManager#ServerGlobalTransportPropertyListener负责,用法见示例:
@2 初始化集群流控server,使用netty通信
三、client/server交互
1.client发送令牌请求
@1 构造请求参数FlowRequestData
flowId:流控规则ID全局唯一标识
count:需要的令牌数量,例如:QPS请求数
priority:优先级,如果当前时间窗口令牌不够,是否预占用下个时间窗口的令牌,默认false。
@2 通信通过netty向server端请求,返回结果TokenResult参数
status:返回状态,见TokenResultStatus枚举类。
remaining:集群流控剩余令牌的数量
waitInMs:当prioritized设置为true时,下个时间窗口分配令牌,需要等待的时间,单位毫秒
attachments:保留扩展字段,未使用
@3 记录错误日志,日志文件为sentinel-cluster-client.log
2.server处理令牌请求
处理请求入口
处理请求过程
@1 请求允许通过尝试,GlobalRequestLimiter负责执行。当监听到namespace有新增时,ServerNamespaceSetPropertyListener#configUpdate更新生效。
通过namespace获取全局RequestLimiter,默认10个采样窗口,统计区间为1秒。
ServerNamespaceSetPropertyListener更新过程
@1 集群流控为嵌入模式,默认将appName加入namespace
@2 将过期失效的namespace流控规则移除
@3 构建namespace、flowId、FlowRule、flowIdSet在缓存中的关系以及为每个flowId准备集群流控统计ClusterMetric,详见:FlowRulePropertyListener#applyClusterFlowRule
@4 构建namespace、flowId、ParamFlowRule、flowIdSet在缓存中的关系以及为每个flowId准备集群热点参数流控统计ClusterParamMetric,详见:ParamRulePropertyListener#applyClusterParamRules
@5 RequestLimiter初始化,GlobalRequestLimiter负责集群全局统计限速判断
@2 根据flowId获取对应的集群流量统计信息ClusterMetric @3 计算剩余的允许通过的令牌数 = 允许通过的令牌数阈值 * 限制倍数 - 已经使用的令牌 - 本次需要的令牌
允许通过的令牌数阈值
exceedCount:限制倍数,默认1.0,可由ServerFlowConfig#exceedCount设置
latestQps:已经使用的令牌
acquireCount:本次需要的令牌
@4 剩余令牌大于等于0,允许通过返回OK
@5 如果设置了优先级prioritized,会占用下个时间窗口的令牌
@6 无令牌可用,统计被阻塞请求并记录日志
@7 返回阻塞结果BLOCKED
四、返回结果处理
说明
OK:值为0,正产放行。允许通过
SHOULD_WAIT:值为2,当设置prioritized=true时预占下个时间窗口令牌需要等待的时间。允许通过
NO_RULE_EXISTS:值为3,缓存中根据flowId找不到FlowRule。不允许通过抛出FlowException
BAD_REQUEST:值为-4,flowId为null或者小于0,通过的请求count小于0,即入参问题。不允许通过抛出FlowException
FAIl:值为-1,内部发生异常了。不允许通过抛出FlowException
TOO_MANY_REQUEST:值为-2,本次请求直接超过设置允许的阈值,被拦回。不允许通过抛出FlowException
BLOCKED:值为1,请求被阻塞。不允许通过抛出FlowException
- SDP(9):MongoDB-Scala - data access and modeling
- 数据清理的遗留问题处理(r6笔记第87天)
- 一次DB time抖动发现的expdp的bug(r6笔记第86天)
- Python中map函数
- 10g,11g中数据库静默安装中的细小差别(r6笔记第85天)
- SDP(8):文本式数据库-MongoDB-Scala基本操作
- SDP(7):Cassandra- Cassandra-Engine:Streaming
- TensorFlow实现神经网络入门篇
- 27.反射,类加载器,设计模式,jdk新特性
- SDP(6):分布式数据库运算环境- Cassandra-Engine
- 配置dg broker的问题分析及修复(r6笔记第84天)
- SDP(5):ScalikeJDBC- JDBC-Engine:Streaming
- SDP(4):ScalikeJDBC- JDBC-Engine:Updating
- SDP(3):ScalikeJDBC- JDBC-Engine:Fetching
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Thinking in DAX with PowerBI - 逻辑框架 - 计算逻辑
- Linux并发执行很简单,这么做就对了
- 一条命令让你明白shell中read命令的常用参数
- 防抖与节流
- 一道题理解Linux中sort命令的多个参数
- Centos7服务器下启动jar包项目的最佳方法
- JavaScript易错点(长期更新)
- Centos7.5配置java环境安装tomcat的讲解
- CSS3卡片光照效果
- Linux文本查找命令find的用法详解
- Canvas系列(2):曲线图形
- Shell中去除字符串里的空格或指定字符的方法
- 使用‘fsck’修复Linux中文件系统错误的方法
- linux/OSX中“DD”命令制作ISO镜像操作系统安装U盘的方法
- 《高效能程序员的修炼》读书笔记