Sentinel分布式集群限流实战
概念
1. 资源:Sentinel通过定义资源,定义资源之后,还需要根据资源来定义规则
2. 规则:规则就是要配置限流的规则,例如这个资源通过的QPS有多少,以及这个资源的名称等等。通过规则,Sentinel能够把资源进行保护,避免突发请求过高导致压垮服务器。
定义资源
通过Sentinel提供的API,SphU.entry,我们可以定义要被保护的资源,示例代码如下:
try(Entry entry = SphU.entry("资源名")){
log.info("✅ 集群限流接口调用成功");
}catch (Exception e){
log.error("阻塞");
}
定义规则
规则是为了用来定义资源的限制配置的,例如,限流的阈值,限流的类型等等
重要规则:
| Field | 说明 | 描述 |
|---|---|---|
| resource | 资源名,资源名是限流规则的作用对象 | 上述代码SphU.entry("资源名"),中的资源名就是这里定义的这个资源名称 |
| grade | 限流阈值类型,QPS 或线程数模式 | QPS指的是所定义的资源,要根据每秒可以通过的请求进行限制 |
| count | 限流阈值 | 如果阈值类型配置的是QPS,那么这里就代表是每秒通过请求数量 |
| strategy | 调用关系限流策略:直接、链路、关联 | 默认根据资源本身(直接) |
| controlBehavior | 流控效果(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流 | 默认直接拒绝 |
示例代码:
理解上面规则的定义之后,我们可以通过调用 FlowRuleManager.loadRules() 方法加载定义的规则
private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource(resource);
// Set max qps to 20
rule1.setCount(20);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
Sentinel Dashboard
概念
Sentinel 提供一个控制台,通过控制台可以看到集群通过请求的情况,以及集群中的机器健康状态、集群的限流规则等等。
安装
1. 下载安装包:下载地址
2. 启动指令
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
注意:Sentinel Dashboard安装的话要求jdk1.8版本以上
使用
1. 首页登录
默认账号和密码都是sentinel

2. 查看集群服务器健康状态

3. 查看Token Server和Token Client列表

4. 查看流控规则,通过dashboard也可以手动修改流控规则

5. 在实时监控中心页面上,可以查看到资源的通过QPS数和拒绝的QPS数

集群流量控制
1. 集群限流的方式:
1.1. 单机均摊限流,就是每台服务器都分配同样的QPS数量
1.2. 集群总体限流:集群所有的服务器加起来的总QPS
2. 使用集群流量控制的目的:
2.1 Sentinel可以通过单机阈值的方式,来限制QPS,例如某个接口最大QPS是100,有10台服务器,这10台服务器都会调用这个接口,那么我们可以给每台服务器都分配10个QPS,这样可以保证10台服务器加起来的总QPS不会超过100QPS。
2.2 但是这个模式有一个缺陷,实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。例如,我有五台机器,每台分配了10个QOS,其中两台都只用了5个QPS,其他服务器可能都用满了,但是两台服务器的5个QPS,也没有办法给其他的服务器使用,因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
3. 大致原理(不细究底层算法):
3.1 集群流控中服务器有两种身份:
Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
4. 前置配置工作
4.1 Sentinel从1.4.0版本开始引入集群流控模块,分为以下几个部分
4.1.1 sentinel-cluster-common-default: 公共模块,包含公共接口和实体
4.1.2 sentinel-cluster-client-default: 默认集群流控 client 模块,如果集群要作为Token Client的话,就需要引入这个模块
4.1.3 sentinel-cluster-server-default: 默认集群流控 server 模块,如果集群要作为Token Server的话,就需要引入这个模块
5. 集群启动模式
- 独立模式:即作为独立的 token server 进程启动,独立部署,隔离性好,但是需要额外的部署操作。独立模式适合作为 Global Rate Limiter 给集群提供流控服务。
- 嵌入模式:在此模式下,集群中各个实例都是对等的,token server 和 client 可以随时进行转变,因此无需单独部署。Token Server既可以作为服务端,也可以作为客户端同时接受外部请求,以及进行发放令牌。
集群启动参数
通过以下虚拟机参数,我们可以配置服务器的信息
-Dsentinel.server.mode=false -- 是否主服务器
-Dproject.name=baobanserver -- 项目名称
-Dcsp.sentinel.cluster.client.server.host=127.0.0.1 -- Token Server地址
-Dcsp.sentinel.cluster.client.server.port=18730 -- Token Server的端口号
-Dcsp.sentinel.dashboard.server=127.0.0.1:8080 -- Sentinel Dashboard的地址
-Dcsp.sentinel.log.dir=./logs/client-2 -- 当前服务器日志记录位置
上述虚拟机参数是要在初始化集群配置的时候使用的,示例:
@Configuration
public class SimpleSentinelConfig {
// ======== 核心配置:从启动参数/配置文件读取集群通信参数 ========
// Token Server 通信端口(Server 用,默认18970,需与启动参数一致)
@Value("${csp.sentinel.cluster.server.port:18970}")
private Integer clusterServerPort;
// Token Server 地址(Client 用,需指向 Server 实例的IP)
@Value("${csp.sentinel.cluster.client.server.host:192.168.10.107}")
private String clusterServerHost;
// Token Server 通信端口(Client 用,需与 Server 的 clusterServerPort 一致)
@Value("${csp.sentinel.cluster.client.server.port:18970}")
private Integer clusterServerClientPort;
/**
* Token Server 模式初始化(仅 Server 实例执行,通过虚拟机参数sentinel.server.mode来控制服务器是Token Server还是Token Client)
*/
@Bean
@ConditionalOnProperty(name = "sentinel.server.mode", havingValue = "true")
public CommandLineRunner initTokenServer() {
return args -> {
System.out.println("=== Token Server 模式 - 初始化集群配置 ===");
// 1. 设置当前实例为 CLUSTER_SERVER 模式
ClusterStateManager.applyState(CLUSTER_SERVER);
// 2. 加载集群限流规则到集群规则管理器(关键!)
FlowRule clusterRule = buildClusterFlowRule();
// 2.1 加载到集群规则管理器(Token Server用)
ClusterFlowRuleManager.loadRules("baobanserver",
Collections.singletonList(clusterRule));
// 2.2 也加载到本地规则管理器(Token Server如果不加载本地规则管理器的话,会导致Token Server限流失败)
FlowRuleManager.loadRules(Collections.singletonList(clusterRule));
System.out.println("✅ Token Server 初始化完成:端口=" + clusterServerPort + ",规则已加载");
};
}
/**
* Token Client 模式初始化(仅 Client 实例执行)
*/
@Bean
@ConditionalOnProperty(name = "sentinel.server.mode", havingValue = "false", matchIfMissing = true)
public CommandLineRunner initTokenClient() {
return args -> {
System.out.println("=== Token Client 模式 - 初始化集群配置 ===");
// 1. 关键:设置当前实例为 CLUSTER_CLIENT 模式(核心!否则降级本地限流)
ClusterStateManager.applyState(ClusterStateManager.CLUSTER_CLIENT);
// 2. 配置 Token Server 连接信息(Client 必须指定 Server 的 IP+通信端口)
ClusterClientAssignConfig assignConfig = new ClusterClientAssignConfig();
assignConfig.setServerHost(clusterServerHost);
assignConfig.setServerPort(clusterServerClientPort);
ClusterClientConfigManager.applyNewAssignConfig(assignConfig);
// 3. 配置 Client 通信参数(超时、重试等)
ClusterClientConfig clientConfig = new ClusterClientConfig();
clientConfig.setRequestTimeout(1000); // 请求超时1s
ClusterClientConfigManager.applyNewConfig(clientConfig);
// 4. 加载集群限流规则(Client 侧仅标记集群模式,阈值以 Server 为准)
FlowRule clusterRule = buildClusterFlowRule();
FlowRuleManager.loadRules(Collections.singletonList(clusterRule));
System.out.println("✅ Token Client 初始化完成:连接 Server=" + clusterServerHost + ":" + clusterServerClientPort + ",规则已加载");
};
}
/**
* 构建集群限流规则
*/
private FlowRule buildClusterFlowRule() {
FlowRule clusterRule = new FlowRule();
clusterRule.setResource("clusterApi"); // 资源名
clusterRule.setGrade(RuleConstant.FLOW_GRADE_QPS); // QPS 限流
clusterRule.setCount(100); // 阈值大小
clusterRule.setClusterMode(true); // 开启集群模式(核心)
// 集群规则配置(关键:thresholdType=1 表示集群总阈值)
ClusterFlowConfig clusterConfig = new ClusterFlowConfig();
clusterConfig.setFlowId(1L); // 规则ID,需全局唯一
clusterConfig.setThresholdType(1); // 1=集群总阈值,0=单机阈值
clusterConfig.setFallbackToLocalWhenFail(true); // 被限流的话是否降级
clusterRule.setClusterConfig(clusterConfig);
return clusterRule;
}
}
常见问题排查
服务器失连
1. 检查Sentinel Dashboard连接信息配置是否有配置,或者配置是否正确
2. 检查服务器启动是否成功,可以在 sentinel-record.log 日志文件中,查看服务器在初始化限流配置的时候是否有抛出异常,或者是否正确连接到了Token Server,如果有正确连接到Token Server会打印连接成功
2026-01-13 23:04:03.875 INFO [NettyTransportClient] Successfully connect to server <127.0.0.1:18730>
3. 检查 command-center.log 日志文件中,是否有在不断的打印连接Sentinel Dashboard的信息:
2026-01-13 23:04:35.009 INFO [SimpleHttpCommandCenter] Socket income: GET /metric?startTime=1768316667000&endTime=1768316673000&refetch=false HTTP/1.1, addr: /192.168.232.121
限流不精准
1. 检查Token Server是否只有一台,如果一个集群中有多台Token Server,那么就会导致有多个Server同时在发放令牌,就会导致限流不正确
2. 检查 sentinel-block.log 日志文件中,是否有限流阻塞日志,如果有限流的话,那么限流的日志就会记录在这个文件当中
3. 可以在Sentinel Dashboard中的流控规则是否与配置的QPS阈值一致
4. 如果是集群限流的话,还要保证在初始化限流配置的时候,FlowId是否全局唯一,如果有多个资源对应同一个FlowId的话,可能会导致限流混乱。
5. 滑动窗口配置,这个比较复杂,需要根据项目调整,可以通过配置来调整
clusterConfig.setWindowIntervalMs(1000);
clusterConfig.setSampleCount(5);
windowIntervalMs指的是,一个滑动窗口的长度,默认是1s,sampleCount指的是,一个滑动窗口要分为多少个格子,官网说细粒度越细,精度越高,但是实际测试下来也不一定。
完整示例代码
1. 依赖配置(pom.xml文件)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.maimeng.baobanq</groupId>
<artifactId>baobanserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>baobanserver</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- 升级Spring Boot和Spring Cloud版本 -->
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
<sentinel.version>1.8.6</sentinel.version>
<spring-cloud-alibaba.version>2.2.7.RELEASE</spring-cloud-alibaba.version>
</properties>
<!-- 替换parent的Spring Boot版本 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- 保留集群依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-cluster-client-default</artifactId>
<version>${sentinel.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-cluster-server-default</artifactId>
<version>${sentinel.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 升级Spring Cloud Alibaba版本 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. 定义资源,下面代码通过定义一个接口,用来限制这个接口的请求QPS数量
package org.example.controller;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/api")
public class ClusterLimitController {
@GetMapping("/cluster/limit")
public String clusterLimit(@RequestParam(defaultValue = "test") String param) {
try(Entry entry = SphU.entry("clusterApi")){
log.info("✅ 集群限流接口调用成功");
}catch (Exception e){
log.error("阻塞");
}
return String.format("成功! 序号: , 参数: %s", param);
}
}
3. 规则定义:
package org.example.config;
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientAssignConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfigManager;
import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager;
import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager;
import com.alibaba.csp.sentinel.cluster.server.config.ServerTransportConfig;
import com.alibaba.csp.sentinel.datasource.AutoRefreshDataSource;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;
import java.util.HashSet;
import static com.alibaba.csp.sentinel.cluster.ClusterStateManager.CLUSTER_SERVER;
@Configuration
public class SimpleSentinelConfig {
// ======== 核心配置:从启动参数/配置文件读取集群通信参数 ========
// Token Server 通信端口(Server 用,默认18970,需与启动参数一致)
@Value("${csp.sentinel.cluster.server.port:18970}")
private Integer clusterServerPort;
// Token Server 地址(Client 用,需指向 Server 实例的IP)
@Value("${csp.sentinel.cluster.client.server.host:192.168.10.107}")
private String clusterServerHost;
// Token Server 通信端口(Client 用,需与 Server 的 clusterServerPort 一致)
@Value("${csp.sentinel.cluster.client.server.port:18970}")
private Integer clusterServerClientPort;
/**
* Token Server 模式初始化(仅 Server 实例执行)
*/
@Bean
@ConditionalOnProperty(name = "sentinel.server.mode", havingValue = "true")
public CommandLineRunner initTokenServer() {
return args -> {
System.out.println("=== Token Server 模式 - 初始化集群配置 ===");
// 1. 设置当前实例为 CLUSTER_SERVER 模式
ClusterStateManager.applyState(CLUSTER_SERVER);
// 2. 加载集群限流规则到集群规则管理器(关键!)
FlowRule clusterRule = buildClusterFlowRule();
// 2.1 加载到集群规则管理器(Token Server用)
ClusterFlowRuleManager.loadRules("baobanserver",
Collections.singletonList(clusterRule));
// 2.2 也加载到本地规则管理器(Token Server如果不加载本地规则管理器的话,会导致Token Server限流失败)
FlowRuleManager.loadRules(Collections.singletonList(clusterRule));
System.out.println("✅ Token Server 初始化完成:端口=" + clusterServerPort + ",规则已加载");
};
}
/**
* Token Client 模式初始化(仅 Client 实例执行)
*/
@Bean
@ConditionalOnProperty(name = "sentinel.server.mode", havingValue = "false", matchIfMissing = true)
public CommandLineRunner initTokenClient() {
return args -> {
System.out.println("=== Token Client 模式 - 初始化集群配置 ===");
// 1. 关键:设置当前实例为 CLUSTER_CLIENT 模式(核心!否则降级本地限流)
ClusterStateManager.applyState(ClusterStateManager.CLUSTER_CLIENT);
// 2. 配置 Token Server 连接信息(Client 必须指定 Server 的 IP+通信端口)
ClusterClientAssignConfig assignConfig = new ClusterClientAssignConfig();
assignConfig.setServerHost(clusterServerHost);
assignConfig.setServerPort(clusterServerClientPort);
ClusterClientConfigManager.applyNewAssignConfig(assignConfig);
// 3. 配置 Client 通信参数(超时、重试等)
ClusterClientConfig clientConfig = new ClusterClientConfig();
clientConfig.setRequestTimeout(1000); // 请求超时1s
ClusterClientConfigManager.applyNewConfig(clientConfig);
// 4. 加载集群限流规则(Client 侧仅标记集群模式,阈值以 Server 为准)
FlowRule clusterRule = buildClusterFlowRule();
FlowRuleManager.loadRules(Collections.singletonList(clusterRule));
System.out.println("✅ Token Client 初始化完成:连接 Server=" + clusterServerHost + ":" + clusterServerClientPort + ",规则已加载");
};
}
/**
* 构建集群限流规则
*/
private FlowRule buildClusterFlowRule() {
FlowRule clusterRule = new FlowRule();
clusterRule.setResource("clusterApi"); // 资源名
clusterRule.setGrade(RuleConstant.FLOW_GRADE_QPS); // QPS 限流
clusterRule.setCount(100); // 阈值到校
clusterRule.setClusterMode(true); // 开启集群模式(核心)
// 集群规则配置(关键:thresholdType=1 表示集群总阈值)
ClusterFlowConfig clusterConfig = new ClusterFlowConfig();
clusterConfig.setFlowId(1L); // 规则ID,需全局唯一
clusterConfig.setThresholdType(1); // 1=集群总阈值,0=单机阈值
clusterConfig.setFallbackToLocalWhenFail(true); // 被的话限流是否降级
clusterRule.setClusterConfig(clusterConfig);
return clusterRule;
}
}
4. 配置文件
spring:
application:
name: baobanserver
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080 # dashboard面板地址
更多推荐

所有评论(0)