3.3 四种订阅模式:谁来接货怎么分货


3.3 四种订阅模式:谁来接货怎么分货

本节摘要:独占、故障转移、共享、按键共享——四种订阅模式是四份分工契约,决定同一主题上的消息如何在多个消费者之间分配。本节用同一个订单主题把四份契约逐一跑给你看,并给出按业务需求选模式的速查表。

谁来接货:接货方式的四张面孔

订阅(subscription)在 Pulsar 里是独立于消费者的持久实体:它有自己的名字、自己的消费进度,消费者只是"来上班的工人",订阅才是"岗位本身"。工人可以换班,岗位的进度不清零。四种订阅模式,就是四种岗位的分活规则。

独占(Exclusive):一个订阅只允许一个消费者,后来者直接被拒。适合"绝不允许并行消费"的场景,比如单实例的配置同步器。它是默认模式,也是最容易理解的一种——一个岗位一个人。

故障转移(Failover):一个主消费者干活,若干备消费者待命,主掉线备自动顶上。分活规则是"热备",适合要高可用但不要并行的消费端,比如必须串行处理的账务核对程序。

共享(Shared):任意多个消费者同时接活,消息在它们之间轮转分发,谁闲推给谁。没有顺序保证(某条消息失败重投可能给到别的消费者),但消费并行度直接等于消费者数量,是吞吐优先场景的主力。

按键共享(Key_Shared):折中版——相同消息键固定分给同一个消费者,不同键可分散到不同消费者。既拿到并行度,又保住"同一键内有序"。订单号作键的订单主题配按键共享,就是"同一订单串行处理、不同订单并行处理"的标准答案。

图 3-2 四种订阅模式的分货规则

图 3-2 四种订阅模式的分货规则

一、用代码把四种模式各上一班岗

四种模式的差异落在客户端就是订阅类型一个参数。同一主题上开四种订阅,观察各自行为:

// 独占:第二个同名消费者会连接失败,异常里明确写着“已有消费者” Consumer<String> exclusive = client.newConsumer(Schema.STRING) .topic(TOPIC).subscriptionName("config-sync") .subscriptionType(SubscriptionType.Exclusive) .subscribe(); // 故障转移:两个消费者同订阅名,只有主收到消息 Consumer<String> failover1 = client.newConsumer(Schema.STRING) .topic(TOPIC).subscriptionName("account-audit") .subscriptionType(SubscriptionType.Failover) .subscribe(); Consumer<String> failover2 = client.newConsumer(Schema.STRING) .topic(TOPIC).subscriptionName("account-audit") .subscriptionType(SubscriptionType.Failover) .subscribe(); // 挂着,收不到;主断开后自动接管 // 共享:N 个消费者轮转接活,吞吐随实例数走 Consumer<String> shared = client.newConsumer(Schema.STRING) .topic(TOPIC).subscriptionName("risk-scan") .subscriptionType(SubscriptionType.Shared) .receiverQueueSize(1000) // 每个消费者的预取缓冲 .subscribe(); // 按键共享:键内有序 + 跨键并行 Consumer<String> keyShared = client.newConsumer(Schema.STRING) .topic(TOPIC).subscriptionName("stock-deduct") .subscriptionType(SubscriptionType.Key_Shared) .subscribe();

验证方法很朴素:往主题发同一订单键的连续状态变更,观察 stock-dedust 订阅侧——同一键的处理日志永远出现在同一个实例上;而 risk-scan 订阅侧,同一键的消息可能散在不同实例。两种订阅并存互不影响,这就是"模式属于订阅"的实证。

二、预取与分发:模式之下还有一层水压阀门

共享与按键共享模式下,消费者本地有个预取缓冲(receiverQueueSize):Broker 会提前把一批消息推到缓冲里。预取大了吞吐好、但某实例宕机时缓冲里未处理的消息要等重投,尾延迟变大;预取小了分发更均匀、吞吐让步。积压敏感与延迟敏感的订阅,把预取当第一个调的旋钮。

⚠️ 常见坑:共享模式里,消息处理失败后的重投会换消费者,"失败消息粘住原消费者"的直觉在共享模式下不成立。依赖"失败必须在原地重试"的逻辑(比如本地事务配套处理),要么改用故障转移,要么把重试语义下沉到业务层。

订阅的名字:命名即契约

订阅名(subscriptionName)是本章最容易轻视、后来最容易后悔的细节。订阅是持久实体,名字即契约:叫作 stock-deduct 的订阅一旦建立,它的模式、进度、死信配置都挂在名字上。实践中吃过亏的团队都会沉淀出自己的订阅命名规范,比如"域-用途-模式"三段式(trade-stock-keyshared、analytics-backfill-shared),让值班同学看名字就能预判行为。

两件与名字有关的治理事项必须写进规范。其一,订阅的建立与删除权限:订阅会持久存在并产生积压账目,谁有权建订阅、废弃订阅何时清理,要有专人负责——放任业务随意建订阅,半年后集群里堆满无人认领的"僵尸订阅",每个都在产生积压统计噪音,有的还占着死信配置。其二,模式变更的正确姿势:共享改按键共享这类模式切换不能原地改,正确做法是新建目标模式的订阅、双跑验证、切流、删旧订阅——模式挂在订阅上,订阅不可变,变的是换代。

与订阅名配套的还有 initialSubscriptionName 这类贴心选项:给死信主题自动建检查订阅,方便发现即检查。这些小配置单看不起眼,攒起来就是运维顺滑度与混乱史的分水岭。

本节要点回顾

  • 订阅是持久岗位,消费者是流动工人;模式是岗位的分活规则;
  • 独占一人、故障转移热备、共享轮转无序、按键共享键内有序跨键并行;
  • 选型两问:要不要并行、要不要键内有序;
  • 同一主题可并存多订阅多模式,进度各自独立;
  • 预取缓冲是共享族模式的吞吐与延迟平衡旋钮。

下一站处理漂流中最微妙的手续:签收、重投与顺序保障——不丢与不重的永恒谈判。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U