From 揭秘JDQ限流架构:实时数据链路的多维动态带宽管控|京东零售技术实践
Kafka 限流介绍
Kafka 可以针对 Producer → Broker 与 Broker → Customer 进行限流。Kafka 原生的限流机制是配额优先级限流机制,kafka 提供两个配额配置参数,三种粒度来进行限流管理:
两个配置参数(可以动态调整):
- producer_byte_rate:生产者单位时间(每秒)内最高允许发送到单台 broker 的字节数。
- consumer_byte_rate:消费者单位时间(每秒)内最高允许从单台 broker 拉取的字节数。
三种粒度:
- user(认证方式)
- client.id(客户端标识)
- user + client.id
user 只能在集群中开启身份认证鉴权的情况下使用,在每个 broker 的 ProduceRequest 和 FetchRequest 中携带的 client/user 客户端身份标识,进行对应的限流。
如果同时配置了多粒度的参数,限流优先级从高到低如下:
/config/users/<user>/clients/<client-id> # 指定的 user+client-id的配置值 那么优先级最高;
/config/users/<user>/clients/<default> # 指定user配置+clients的默认值
/config/users/<user> # 单独的user粒度,指定user
/config/users/<default>/clients/<client-id> # user的默认值+指定client-id
/config/users/<default>/clients/<default> # user的默认值+默认的client-id
/config/users/<default> # 单独的user的粒度,所有user的默认值
/config/clients/<client-id> # 单独的client-id粒度,指定client-id
/config/clients/<default> # 单独的client-id粒度,所有client的默认值,优先级最低限流算法:我们假设当前实际速率是 O,T 是预设的 user 限流速率值(可以根据实际情况配置),而 W 表示某一段时间范围,我们希望在 W 时间内 O 能够下降到 T 以下(如果 O 本来就比 T 小,则什么都不用做),那么 broker 端就需要延缓等待一段时间后再响应请求。如果假设这段时间是 X,那么以下等式成立: 。在具体实现时,Kafka 提供了两个参数来共同计算 W:。前者表示取样的时间窗口个数,后者表示时间窗口大小。
超额处理:消息队列本身的功能是削峰填谷,在有突发流量的时候,流量很容易超过配额。此时,机器层面一般是有能力处理流量的,如果直接拒绝流量,就会导致消息投递失败,客户端请求异常。所以,在限流后,Kafka 的处理方式是延时回包,通过加大单次请求的耗时,整体上降低集群的吞吐。因为正常状态下,客户端和服务端的连接数是稳定的,如果提升单次处理请求的耗时,集群整体流量就会相应下降。增加的耗时时长就是使用上述的限流算法计算的。
Kafka 限流机制的局限性
Broker-Topic 维度限流缺陷:节点故障 leader 切换引发的速率波动
当 user1 对 topic1 授予了 producer 的权限,user1 的单机生产限流配额 producer_byte_rate 为 10M/s,那使用 user 通过认证的生产客户端可以往 topic1 里的每个分区生产数据,那么每个分区的峰值流量都为 10M/s。分区色块为粉红色的是 leader 节点。
当 user1 对 topic2 授予了 producer 的权限,user2 的单机生产限流配额 producer_byte_rate 为 10M/s,那使用 user 通过认证的生产客户端可以往 topic1 里的每个分区生产数据,那么分区 1,2 共享限流为 10M/s。
如果 Kafka 集群的某个 Topic Leader 在发生故障切换时,会对生产与消费速率产生的间接影响,暴露了现有限流机制的一个短板。在标准配置下,生产者消费者的吞吐量分配与分区 Leader 的物理分布密切相关。
具体而言,假设一 Topic 拥有 m 个分区,初始分布于 n 个活跃 Broker 之上,每个 Broker 承载 m/n 个分区的 Leader。消费者对于该 Topic 的限速配额设定为 s MB/s,理论上可实现总吞吐量 s*n MB/s。然而,一旦某 Broker 遭遇故障,Leader 角色将重新分配至剩余 n-1 个 Broker,尽管整体分区数保持不变,但限速原则却按 s*(n-1) MB/s 重新计算,导致吞吐量骤减。这一现象表示 Kafka 限流算法在适应动态故障场景时的脆弱性,用户需承受非预期的消费速率下降及潜在的数据积压风险。
缺乏单机限流机制与实时弹性调节能力
Kafka 现行限流机制聚焦于 User-Client 层面,忽视了单机 Broker 的容量限制,从而在面对这个 broker 下的 user 的生产/消费的总速率超过单机硬件限制的理论带宽上限的情况时,只能手动向下调整平台上与 broker 有关的生产者,消费者的配额参数,而 Kafka 集群本身并不会做出什么相应的限流举动,任由过载状态持续影响所有业务,直至触发网络拥塞或数据丢失。同时,Kafka 限流机制高度依赖于预先设定的业务系统限流配额,无法依据实时网络状况或 Broker 负载动态调整对应的生产消费配额,削弱了系统的弹性和响应性。
资源分配非最优与业务优先级处理缺失
当前限流技术在自动化处理业务重要性等级方面存在短板,未能充分考虑到不同业务场景的独特性。特别是在资源竞争激烈的环境中,该技术未能针对不同业务的关键程度做出有效区分。当达到限流阈值时,所有业务均遭受无差别的限制,忽视了高优先级服务的特殊需求。这种粗放式的处理方式,不仅无法满足特定业务场景的个性化需求,还可能阻塞关键业务流程或降低用户体验,进而引发用户的不满和投诉。在当前强调成本控制和效率提升的大环境下,迫切需要一种解决方案,能够在资源紧张时优先保障高优先级业务,通过错峰生产/消费模式,实现资源的合理配置和高效利用,以实现最大价值。
综上:
- 维度单一:限流策略过于粗放,未能覆盖分区级别或单 Broker 层级的精细化控制;
- 缺乏实时弹性:依赖预设限流配额,无法根据实时业务情况进行动态自动调整。
- 未区分业务优先级:未能根据业务的重要性和紧急性进行差异化处理,影响了流量资源的最优配置。
JDQ 限流机制
多维度的精细化限流粒度
在 Kafka 的基础上,JDQ 平台支持更精细化粒度限流,即分区级别限流,可以让生产消费的吞吐量都不受故障节点影响而降低。
核心逻辑为:在 Controller 发起的元数据更新请求中,记录下来 Broker 上每个 Topic 对应的 leader 数量,在计算消费等待时长时,会让消费限速配额 consumer_byte_rate * 该 Topic 在分区 Leader 数量,从而实现不论 Topic 的分区 Leader 分布在几台机器上,消费者或者生产者的总速率都能保持不变。
改造后的限流原则在节点故障前后均用 s*m MB/s 计算,使得速率恒定为配额*分区数,进而解决机器故障是对生产/消费的吞吐量的影响。
单机限流和分级动态弹性限流
L1、L2、L3 的限流逻辑为,根据为每个等级下分配的被分级限流时不同的带宽配额(可动态改配),以及分区粒度的限流算法进行计算等待时间,使对应等级的业务进行限流;
这一架构的核心在于引入单机带宽使用阈值,以及重要性等级机制(在分区限流的基础上),为不同等级(L0,L1,L2,L3)的生产者/消费者(即业务系统)分配差异化的限流带宽配额。这些参数可以支持动态配置地传入 Kafka 集群服务端,使得集群能够实时根据单机带宽使用情况,自动、弹性地调整对各个重要性等级业务系统的限流与恢复策略。具体来说,当单机带宽使用超过预设阈值时,Kafka 集群将依据重要性等级从低到高,分级实施限流措施,确保高重要性业务系统得到优先保障。反之,当带宽使用回落到安全范围内时,系统将自动恢复限流,保障业务系统的顺畅运行。
后续优化方向
- 多形式多粒度的限流粒度:未来优化将着眼于更多形式的限流,比如根据 QPS 限流,更细粒度的限流,如消息类型的限流,从而更好地满足多样化的业务需求和资源分配策略。
- 基于容器化的带宽弹性伸缩:进一步探索 JDQ 与容器技术的深度融合,实现集群的按需弹性伸缩。用户限速带宽可以根据平时的实际使用率自动调整,确保资源的高效利用与成本控制,同时提升系统的整体响应能力和弹性。
- 智能化限流规划与研发:为了进一步降低运维复杂度,提升系统可靠性,未来将加大投入于智能化限流方案的研发,实现限流策略的自动优化,减少人为干预,提升运维效率