集群流量控制
文章系列
【一、Alibaba Sentinel 基操】
【二、Alibaba Sentinel - 集群流量控制】
【三、Alibaba Sentinel - 工作流程及原理解析】
【四、Alibaba Sentinel - Slot chain解析】
【五、Alibaba Sentinel - 滑动窗口】
一、集群流控介绍
集群流控可以解决流量不均匀导致总体限流效果不佳的问题。
假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。
因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
集群流控中共有两种身份:
- Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
- Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
二、整体架构
三、Sentinel 集群限流
- 创建集群限流客户端
- 创建集群限流服务端
- 配置限流规则(Nacos配置中心)
整体架构如下:
3.1 集群限流客户端
项目结构
3.1.1 添加动态获取限流规则InitFunc
/**
* 从nacos上动态获取限流规则
* 注意:需通过 SPI 机制添加
*/
public class DataSourceInitFunc implements InitFunc {
// token server 地址
private final String CLUSTER_SENTINEL_HOST = "127.0.0.1";
// token server 端口
private final int CLUSTER_SERVER_PORT = 9999;
// 请求超时时间
private final int REQUEST_TIMEOUT = 20000;
// dataId
private final String APP_NAME = "token-server";
// nacos远程服务地址
private static final String NACOS_REMOTE_ADDRESS = "localhost:8848";
// nacos groupId
private static final String GROUP_ID = "sentinel-group";
@Override
public void init() throws Exception {
// 加载集群信息
loadClusterClientConfig();
// 注册集群限流规则属性:故障转移方案,当token server不用时,直接从 动态限流规则配置中心获取限流规则
registryClusterFlowRuleProperty();
}
private void loadClusterClientConfig() {
// 设置 token server 集群信息
ClusterClientConfigManager.applyNewAssignConfig(new ClusterClientAssignConfig()
.setServerHost(CLUSTER_SENTINEL_HOST)
.setServerPort(CLUSTER_SERVER_PORT));
// 设置超时时间
ClusterClientConfigManager.applyNewConfig(new ClusterClientConfig()
.setRequestTimeout(REQUEST_TIMEOUT)
);
}
private void registryClusterFlowRuleProperty() {
ReadableDataSource<String, List<FlowRule>> readableDataSources = new NacosDataSource<List<FlowRule>>(
NACOS_REMOTE_ADDRESS,
GROUP_ID,
APP_NAME,
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
FlowRuleManager.register2Property(readableDataSources.getProperty());
}
}
3.1.2 指定客户端角色
// 指定角色:表示当前的节点是集群客户端
ClusterStateManager.applyState(ClusterStateManager.CLUSTER_CLIENT);
3.1.3 添加InitFunc SPI
创建文件:META-INF\services\com.alibaba.csp.sentinel.init.InitFunc
文件内容:
com.tortoise.tokenclient.DataSourceInitFunc
3.2 集群限流服务端
项目结构
3.2.1 引入相关依赖
创建一个 Maven 项目,引入以下依赖。
<dependencies>
<!--Sentinel server端依赖-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-cluster-server-default</artifactId>
<version>1.8.4</version>
</dependency>
<!--Sentinel 限流规则数据源-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.4</version>
</dependency>
</dependencies>
3.2.2 创建 SentinelTokenServer
public class SentinelTokenServer {
private static final String APP_NAME = "sentinel-token-server";
public static void main(String[] args) throws Exception {
ClusterTokenServer tokenServer = new SentinelDefaultTokenServer();
ClusterServerConfigManager.loadGlobalTransportConfig(new ServerTransportConfig()
.setIdleSeconds(600)
.setPort(9999));
ClusterServerConfigManager.loadServerNamespaceSet(Collections.singleton(SentinelTokenServer.APP_NAME));
// Start the server.
tokenServer.start();
}
}
3.2.3 添加动态获取限流规则InitFunc
/**
* 从nacos上动态获取限流规则
* 注意:需通过 SPI 机制添加
*/
public class DataSourceInitFunc implements InitFunc {
// nacos远程服务地址
private static final String NACOS_REMOTE_ADDRESS = "localhost:8848";
// nacos groupId
private static final String GROUP_ID = "sentinel-group";
@Override
public void init() throws Exception {
ClusterFlowRuleManager.setPropertySupplier(dataId -> {
ReadableDataSource<String, List<FlowRule>> readableDataSources = new NacosDataSource<List<FlowRule>>(
NACOS_REMOTE_ADDRESS,
GROUP_ID,
dataId,
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
return readableDataSources.getProperty();
});
}
}
3.2.4 添加InitFunc SPI
创建文件:META-INF\services\com.alibaba.csp.sentinel.init.InitFunc
文件内容:
com.tortoise.tokenserver.DataSourceInitFunc
3.3 Nacos配置限流规则
在Nacos创建对应的配置,添加JSON格式内容如下:
[
{
"resource": "resource-name",
"grade": 1,
"count": 100,
"clusterMode": true,
"clusterConfig": {
"flowId": 1,
"thresholdType": 1,
"fallbackToLocalWhenFail": true
}
}
]
四、集群流控规则
FlowRule 添加了两个字段用于集群限流相关配置:
private boolean clusterMode; // 标识是否为集群限流配置
private ClusterFlowConfig clusterConfig; // 集群限流相关配置项
其中 用一个专门的 ClusterFlowConfig
代表集群限流相关配置项,以与现有规则配置项分开:
// (必需)全局唯一的规则 ID,由集群限流管控端分配.
private Long flowId;
// 阈值模式,默认(0)为单机均摊,1 为全局阈值.
private int thresholdType = ClusterRuleConstant.FLOW_THRESHOLD_AVG_LOCAL;
private int strategy = ClusterRuleConstant.FLOW_CLUSTER_STRATEGY_NORMAL;
// 在 client 连接失败或通信失败时,是否退化到本地的限流模式
private boolean fallbackToLocalWhenFail = true;
flowId
代表全局唯一的规则 ID,Sentinel 集群限流服务端通过此 ID 来区分各个规则,因此务必保持全局唯一。一般 flowId 由统一的管控端进行分配,或写入至 DB 时生成。thresholdType
代表集群限流阈值模式。其中单机均摊模式下配置的阈值等同于单机能够承受的限额,token server 会根据客户端对应的 namespace(默认为 project.name 定义的应用名)下的连接数来计算总的阈值(比如独立模式下有 3 个 client 连接到了 token server,然后配的单机均摊阈值为 10,则计算出的集群总量就为 30);而全局模式下配置的阈值等同于整个集群的总阈值。
ParamFlowRule 热点参数限流相关的集群配置与 FlowRule 相似。