资讯详情

集群限流高可用:Token Server主备切换与Nacos配置同步

📅 2026/10/8 4:08:24 | 华诺云谱 👁 阅读
集群限流高可用:Token Server主备切换与Nacos配置同步
从单机限流在流量不均匀场景下经常误伤这点切入把集群限流的必要性讲透再逐步展开 Token Server 的部署难点和高可用方案。这篇我会结合实际踩坑经历讲一下 Token Server 独立部署、Nacos 配置同步、以及客户端报错排查的完整链路。1. 为什么单机限流不够用流量不均带来的“误伤”先说一个很多团队都会遇到的问题明明配置了限流规则压测和线上表现却完全不是一回事。我们曾经有个订单查询服务单机 QPS 上限设了 500总共 5 台机器理论上集群容量是 2500 QPS。但实际情况是流量分布根本没有我们想的那么均匀高峰期某一台机器的 QPS 可能直接冲到 1200另外几台还在 200 到 300 徘徊。结果就是那台机器疯狂触发限流大量请求被拒绝用户看到的就是接口时不时报错但我们集群整体的 QPS 其实还远没到瓶颈。这就是单机限流的死穴限流规则只对单台机器生效和集群真实的容量评估存在偏差。你按单机能力设置阈值流量一旦倾斜好机器闲着忙机器被打爆限流策略形同虚设。如果你为了照顾单机峰值把阈值调大那遇到流量均匀的时候整体超限又没法感知风险更不可控。集群限流要解决的就是这个问题。核心思路很简单把原本分散在各台机器本地内存里的流量统计收敛到一个统一的计数中心去所有请求到达业务机器之后先去这个计数中心“报个数”确认没有超过集群总阈值再放行。这样一来不管流量怎么倾斜只要整个集群的总 QPS 超过预设值就会被拦住而不是等着某一台机器被单独打爆。说到这儿就要引出 Sentinel 集群限流里的两个角色Token Server 和 Token Client。Token Client 内嵌在业务应用里负责采集请求并把令牌申请请求通过网络发给 Token ServerToken Server 是独立的进程或嵌入在某台机器里它维护着全局的计数器根据规则决定给不给令牌。整体的请求链路大概是这样业务请求进来 - Token Client 向 Token Server 申请令牌 - Token Server 根据集群统计结果返回放行或拒绝 - 业务拿到结果后继续或降级。这个模型的好处是计数逻辑集中可管理缺点也立刻暴露出来——Token Server 成了整个限流体系里的单点。它一旦挂了或者网络不通要么所有请求因为没有拿到令牌被熔断降级要么 Token Client 进入了 fallback 逻辑直接放行导致限流形同虚设。无论哪种都是生产事故。所以标题里的“高可用部署方案”才是真正的重头戏后面我详细拆。2. Token Server 高可用的真正难点计数器在内存里我在跟很多同行聊集群限流的时候发现一个共同误区大家以为高可用就是多部署几台 Token Server前面再加个负载均衡就完事了。实际上如果这么简单Sentinel 官方文档里早就给出完整集群方案了。真正的难点在于Sentinel Cluster Token Server 的流控计数器是完完全全存在内存里的它不是无状态服务你跟它谈简单横向扩容是谈不动的。2.1 为什么说 Token Server 是有状态服务Sentinel 集群限流默认的计数实现是 AtomicInteger 之类的内存变量。每个滑动窗口的计数、每个资源的限流状态、每一条 Rule 的统计结果都在这台 Token Server 的 JVM 堆内存里。如果我有两台 Token Server一台统计到 QPS 800另一台统计到 QPS 500而我们的集群总阈值是 1000请问到底听谁的没有一台全局的协调者来合并双方的计数这种“双主”状态就是对限流逻辑的根本性破坏。所以 Sentinel 官方根本没有提供类似 Consul、Etcd 那种基于 Raft 协议的集群方案它只把 Token Server 划分成两种部署角色嵌入式模式是挂在某台业务机器上独立模式是单独起的进程。但无论哪种方式同一时刻对于同一组资源真正有效处理令牌申请的只能有一个逻辑计数中心。如果你硬要在两台机器上同时起两个 Token Server并且让不同业务机器各连各的那本质上就是两套独立的限流系统不能叫“集群限流”。2.2 高可用不等于并行扩展而是主备切换这里必须把概念掰清楚。Token Server 高可用部署的合理形态不是多活并行而是“主备 快速切换”。主节点负责维护实时计数器和处理令牌请求备节点保持存活、维持规则同步和心跳监测一旦主节点不可用流量立刻切换到备节点。这个方案看起来像“降级”因为备节点在切换前并没有实时统计能力切换后计数器从零开始会有一个精度漂移的窗口期。但考虑到限流本身是对突发流量的保护短时间内的计数重置对大多数业务场景是可以接受的。你需要做的是在切换完成后让规则以最快速度在备节点上生效。结合 Nacos 做规则动态推送这个收敛时间可以被压缩到秒级甚至毫秒级完全可以接受。这里我必须说一句如果业务对限流的精度要求苛刻到连切换那几秒的计数偏差都不能容忍那你应该考虑外部化存储来实现集群限流比如用 Redis 做计数、用 Sentinel 的扩展接口去适配但这已经偏离了 Sentinel 开箱即用的玩法属于定制开发范畴。我先说这个是因为你不能指望官方方案解决 100% 场景。3. 独立模式 负载均衡 Nacos 的落地部署明确了“主备 切换”的思路接下来就是具体怎么落。我这个方案选择了独立模式部署 Token Server搭配负载均衡的四层转发来屏蔽后端节点变化再通过 Nacos 做配置下发的自动化。整套架构部署下来业务无感知运维可掌控扩展性上也留了余地。3.1 架构与组件清单先说清楚整套方案里都有什么角色每个角色干什么活了再画一下拓扑关系。组件角色说明Token Server A主节点独立进程运行 Sentinel 集群 Token Server维护实时计数器Token Server B备节点独立进程与 A 保持规则同步随时准备接管流量负载均衡流量的统一入口四层 TCP 转发把 11111 端口流量分发给后端 Token Server开启健康检查业务应用Token Client 角色内嵌 Sentinel 集群客户端通过负载均衡 VIP 访问 Token ServerNacos规则配置中心存储流控规则动态推送给 Token Server 和业务应用Token Server A 和 B 之间的漂移逻辑是利用负载均衡的健康检查实现的。默认流量全部转发到主节点主节点返回不正常时负载均衡自动把它摘除后续的新连接全部打到备节点上。备节点从“冷备”变为“热备”开始处理令牌申请。独立模式的启动方式比嵌入式要清爽很多你是单独起一个 Spring Boot 工程也好还是用 java -jar 起一个普通 jar 包也好关键在于设置一个系统参数-Dcsp.sentinel.cluster.server.port11111这个参数指定了 Token Server 监听客户端连接请求的端口。同时独立模式下还会占用一个 HTTP 端口用于 Sentinel 控制台通信通常我们在独立模式下再指定-Dserver.port7777如果是在 Kubernetes 环境部署要特别注意 Service 的网络模型。Token Server 对客户端提供服务走的是 11111 TCP 端口控制台探测走 7777 HTTP 端口两个端口的健康检查探针要分开处理。很多人只配了一个 HTTP 存活探针结果 11111 端口实际已经挂了但探针检测不到服务一直不摘除客户端全部卡死。3.2 服务端部署步骤我以 Spring Boot 工程为例跑通一个最小可用的独立 Token Server。工程里只引入 Sentinel 集群相关的几个核心依赖dependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-cluster-server-default/artifactId /dependency dependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-transport-simple-http/artifactId /dependency dependency groupIdcom.alibaba.nacos/groupId artifactIdnacos-client/artifactId /dependency启动类里最关键的一步是手动把 Token Server 处于运行状态。Sentinel 集群模块默认是不会自动把服务端角色拉起来的必须调用启动函数SpringBootApplication public class TokenServerApplication { public static void main(String[] args) { SpringApplication.run(TokenServerApplication.class, args); // 启动集群 Token Server ClusterServerStateManager.applyState(ClusterServerStateManager.SERVER_STATE_STARTED); } }我没有把启动逻辑放在 CommandLineRunner 里而是放在 main 方法中直接触发原因是为了保证 Spring 上下文完全就绪后再启动 Token Server避免出现端口已经监听但规则还没加载的中间状态。然后在 Nacos 配置中心准备集群 Token Server 的全局配置。这里需要创建 namespace、group、dataId 三个维度都正确的配置文件我在第 4 节专门讲因为这里翻车的人实在太多了。规则配置准备好之后还需要一个监听 Nacos 配置变化的代码段把动态下发的规则实时注册到 Sentinel 的规则管理器中ReadableDataSourceString, ListFlowRule flowRuleDataSource new NacosDataSource( 限流配置的namespace, DEFAULT_GROUP, 限流规则dataId, source - JSON.parseObject(source, new TypeReferenceListFlowRule() {}) ); FlowRuleManager.register2Property(flowRuleDataSource.getProperty());这一段代码的作用就是把 Nacos 上配置的流控规则文件实时同步到 Token Server 内存里。监听到文件变化后Sentinel 内部会自行更新 Rule 管理器中的内容不需要改代码、不需要重启。主备两台 Token Server 都要配这套监听逻辑这样才能保证备节点被切换上位时规则是最新状态。3.3 客户端接入配置业务应用作为 Token Client接入的关键是让它的令牌申请请求能稳定到达 Token Server。在 Sentinel 的客户端上你需要让集群限流模块处于客户端模式同时给它指定一个可以到达负载均衡 VIP 的访问配置。客户端 yml 里或者启动参数里有几个关键配置我直接列出来# 集群客户端模式 csp.sentinel.cluster.client.assign-typestatic # 指向负载均衡地址而不是直接指向某一个 Token Server 节点 IP csp.sentinel.cluster.client.server-host你的负载均衡VIP地址 csp.sentinel.cluster.client.server-port11111 # 连接 Token Server 的请求超时时间默认给个 200ms 比较稳妥 csp.sentinel.cluster.client.request-timeout200这里有一个非常容易被忽略的点我见过很多团队直接把 server-host 配成了主 Token Server 的 IP。这种配置工作正常的前提是主节点永远在线。一旦主节点宕机客户端不会自动感知后端有一个备节点可用因为客户端这边压根不知道备节点的存在它只会拼命重连那个已经不通的 IP。每次重连失败的等待时间就是一批请求被降级或者报错的时间。所以墙裂建议把 VIP 作为客户端唯一的访问入口。负载均衡背后挂了几个 Token Server、哪个是主、哪个是备这个信息对客户端完全透明。客户端只跟 VIP 建立长连接TCP 四层转发会把连接调度到当前健康的后端节点上。这样主备切换是运维层面的事业务应用无感。4. 与 Nacos 集成的规则同步细节如果你对 Sentinel 和 Nacos 的集成不太熟这一节建议反复看因为真的太多人在 Nacos 的 namespace、group、dataId 三个维度上踩坑。之前有网友提到“sentinel 限流配置 nacos 样例”核心诉求就是想要一套可以直接抄的完整配置例子。我这里给出我自己线上跑通的方案。4.1 完整可复用的规则同步配置先说命名空间设计。我在 Nacos 里专门为 Sentinel 建了一个独立的 namespace比如叫 sentinel-config。这样做的好处是把限流规则和业务配置隔离权限管理也能单独控制。然后创建两个 dataId分别对应流控规则和集群配置namespace: sentinel-config group: DEFAULT_GROUP dataId: sentinel-app-flow-rules规则文件的内容格式是这样的我以一条集群流控规则为例[ { resource: order:queryOrderInfo, grade: 1, count: 5000, clusterMode: true, clusterConfig: { flowId: 1001, thresholdType: 1, fallbackToLocalWhenFail: true, sampleCount: 10, windowIntervalMs: 1000 } } ]这里面几个关键字段必须解释清楚clusterMode设为 true表示这是集群限流规则不设这个字段客户端和服务器都不知道要走集群模式thresholdType是 0 还是 1 决定了规则是单机均摊还是集群总阈值一般我们设 1代表聚合到 Token Server 的全局计数fallbackToLocalWhenFail这个字段尤其要留意它决定了当 Token Server 无法连通时是放行到本地单机限流兜底还是直接拒绝。考虑到业务可用性优先的原则我通常把它设为 true请求会被本地规则接住而不是一窝蜂被拒虽然没有全局限制那么精准但至少不会因为限流组件自身的故障拖垮所有业务。这条规则文件通过 Nacos 发布后第 3 节里写的 NacosDataSource 监听代码就会自动感知完成推送。业务侧和 Token Server 侧监听的 dataId 保持统一保证规则一致。4.2 namespace、group、dataId 不一致的典型翻车现场我第一次搭这套环境时也翻车了。当时我发现规则只推送到了业务应用Token Server 压根没收到日志里也没有任何 Nacos 配置更改的记录。后来反复排查发现一个问题业务应用读取配置时用的是 Nacos 默认 namespaceToken Server 的客户端配置文件里指定了namespacesentinel-config二者各监听各的自然没法互通。Nacos 的 namespace 一旦填错或没填客户端就会连到一个完全不同的命名空间不会自动融合更不会有任何报错提示。还有 group 的问题。Nacos 默认的 group 是 DEFAULT_GROUPSentinel 控制台生成的一些规则可能塞在 SENTINEL_GROUP 里。如果在客户端代码里没显式指定 group它默认走的是 DEFAULT_GROUP那你去监听一个 SENTINEL_GROUP 下的 dataId永远都拿不到数据。所以我的建议是不管是在代码里还是在 Nacos 控制台上把 namespace、group、dataId 的取值都显式写出来、写成常量别依赖默认值。好记性不如烂笔头这些隐性默认值是配置事故的高发源头。5. token exchange failed 类报错的排查链路有网友反馈过“登录失败:login server error: token exchange failed: token endpoint returned”这样的报错。这个错误字符串在网络上经常出现在 OAuth、网关登录鉴权场景但在集群限流体系里它对应的本质问题和 Login 无关而是 Token Client 与 Token Server 之间的通信握手、令牌申请这一环出了问题。你抓住“token exchange failed”这个短语结合上下文去理解就知道是客户端在向令牌服务端要令牌时失败了。5.1 报错现象与常见误导我第一次遇到这类报错时当时的反应是查 Nacos、查规则推送全都没问题。后来发现掉进了惯性思维的坑——因为报错信息里有 “login” 和 “token” 这种词汇人本能会往认证授权方向查绕了一大圈才意识到问题出在 Sentinel 集群通信层。在 Sentinel 的日志体系里Token Client 申请令牌失败时会输出类似这样的日志[SentinelClusterClient] Request token failed, server: xxx.xxx.xxx.xxx:11111, params: xxx, error message: token exchange failed以及[SentinelClusterClient] [Priority] Server 状态异常切换到本地限流发生这两种日志的时候业务方看到的现象大概率是接口偶发限流、响应变慢、请求被降级。尤其当fallbackToLocalWhenFail设为 false 时你还会看到大量请求直接被拒这时业务端报错明显增加。5.2 从连接握手开始逐层排查我自己的排查顺序是固定的从链路最底层往上层查。先把负载均衡这一层的 TCP 连通性排除掉telnet 负载均衡VIP 11111如果连不通先查负载均衡后端节点的健康状态是四层健康检查把主节点摘除了还是主节点服务真的挂了。这个阶段不要急着去动应用配置很多时候问题根本不在业务侧。如果连通性没问题再往下就是 Token Client 的配置问题。优先检查客户端有没有正确指向负载均衡 VIP。有人图省事直接复制了某个 Token Server 节点 IP 到配置里导致主节点切换以后客户端依旧在向老 IP 发起连接。这种情况在日志里的表现是连接超时、反复重连而不是直接报连接拒绝。最后一步才是查规则层面。打开 Sentinel 控制台看 Token Server 节点是否注册成功、是否处于健康状态以及集群配置中的 flowId 是否和业务侧使用的资源对应。很多时候客户端申请令牌时带上的规则 ID 在服务端根本不存在服务端返回找不到规则客户端就把它视为 token exchange failed。这个问题在控制台上很好定位线上日志里却不明显。5.3 主备切换时的连接中断处理我下面再分享一个实战中频繁出现的场景主 Token Server 所在的机器因为发布重启负载均衡把连接切到备节点但这瞬间正在处理中的令牌申请请求会立刻失败。此时客户端的报错不是超时而是直接 RST。这里需要明确一个取舍如果你不允许这一秒内任何请求失败可以在客户端把请求超时时间调长一点但代价是正常的限流拒绝也会变慢业务等待时间变长。我个人的做法是接受切换那一瞬间的少量失败但把客户端的重试机制做好。Sentinel 的 Token Client 本身不具备自动重试逻辑如果你用的是服务框架可以在框架层面做一次重试。但必须注意重试只适用于读操作像下单、转账这类写操作重试会造成重复提交。因此我还是建议在 Token Server 切换时配合发布流程控制先摘流量、再重启、然后重新放流量。不要让切换发生在一个请求还在途中的瞬间。这不光是限流组件的问题任何有状态服务做发布都应该遵循这个原则。6. 高可用验证与我的实践心得部署方案设计得再完整最终还是要靠故障演练来验证。我每次搭完这套环境都会分三步做验证。第一步是压测用固定 QPS 打业务服务同时观察控制台上的集群总流量数值确认阈值生效且统计准确。第二步是模拟主节点宕机手动杀掉 Token Server 主节点进程观察负载均衡是否在健康检查间隔内摘除节点客户端是否自动切换到了备节点以及切换后集群限流是否继续生效。第三步是重新启动主节点验证服务恢复后流量是否自动回切。这三步里最值得关注的是切换时间窗口。负载均衡的四层健康检查目前我是 2 秒一次检测到主节点异常之后摘除节点再到客户端重连备节点整体耗时在 3 到 5 秒之间。这个窗口期内集群限流精度下降但不至于中断。如果你的业务对连续性要求更高健康检查间隔可以缩到 1 秒代价是负载均衡的检测压力增大这个根据团队运维能力来权衡。部署完成后每天都会自动跑一次巡检脚本探测 Token Server 进程状态、端口监听状态、规则更新时间、客户端连接数这几个指标任何一个异常都会触发告警。这套体系上线后集群限流导致的接口故障基本绝迹而且后续再调整限流阈值时再也不用一台台机器改配置在 Nacos 上改完直接全集群生效。最后再分享一个小技巧如果你给业务应用也挂上了 NacosDataSource那更新限流规则的时候有个并发问题要小心。Sentinel 的规则更新是异步的新规则要等当前时间窗口结束后才彻底生效。所以在 Nacos 上发布新规则之后不要立刻用压测去验证效果等一个完整的滑动窗口周期再检查否则你看到的还是一个过渡态的统计结果容易误判规则配置有问题。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑