发布者怎么知道 broker 收下了消息?confirm 模式下 Ack 一条一个还是批量?“确认”是指消息进了队列还是落了盘?队列满了会怎样?这篇用 TCP 代理把确认帧打出来,并比较几种发布方式的吞吐。
Confirm.Select / SelectOk 帧共 2 个(两个 channel 各开了一次);发布 10 条(pika BlockingChannel 每条同步等 ack)收到的 Basic.Ack:
Basic.Ack tag=1 multiple=False
Basic.Ack tag=2 multiple=False
Basic.Ack tag=3 multiple=False
Basic.Ack tag=4 multiple=False
Basic.Ack tag=5 multiple=False
Basic.Ack tag=6 multiple=False
Basic.Ack tag=7 multiple=False
Basic.Ack tag=8 multiple=False
Basic.Ack tag=9 multiple=False
Basic.Ack tag=10 multiple=FalseConfirm.Select 把 channel 切成 confirm 模式,此后每条发布在该 channel 里有一个从 1 递增的序号,broker 用 Basic.Ack(成功)或 Basic.Nack(失败)按序号回复。
发 5000 条持久化消息,在途上限 1000:耗时 164 ms;broker 一共发了 619 个 Basic.Ack,其中 multiple=True 的 603 个 每个 Ack 确认的条数: 最小 1,中位数 7,最大 73;前 8 个 Ack: [29, 33, 12, 11, 6, 15, 7, 3]
broker 可以把已处理好的一段序号合并成一个 multiple=True 的 Ack。异步发布时 5000 条只收到 619 个 Ack,中位数每个确认 7 条,所以发布方要维护一个“在途序号集合”,按 multiple 一次性清掉 ≤ tag 的部分。
不开 confirm(阻塞) 132 ms 37800 条/秒 每条同步等 confirm 869 ms 5752 条/秒 在途上限 100 92 ms 54057 条/秒 在途上限 1000 92 ms 54634 条/秒
每发一条就等一个 Ack,吞吐受限于往返次数(5752 条/秒);允许几十条在途后升到 54057 条/秒,100 和 1000 没区别。因此一般做“窗口式”异步发布,而不是逐条等待。
非持久化:p50 = 0.08 ms,p99 = 0.15 ms 持久化:p50 = 0.15 ms,p99 = 0.26 ms
单条同步 confirm 的 p50:非持久化 0.08 ms,持久化 0.15 ms。要说明的是,这是 macOS 笔记本上的数字,其 fsync 不保证落盘,不能当作生产环境的持久化延迟。
mandatory=False 发到没有绑定的 exchange,线上帧: ['Basic.Publish', 'Basic.Ack tag=1 multiple=False'] mandatory=True,线上帧: ['Basic.Publish', 'Basic.Return', 'ContentHeader', 'ContentBody', 'Basic.Ack tag=2 multiple=False'];pika 抛出 UnroutableError
confirm 只说明 broker 处理完这条消息,不保证进了队列:没有绑定的 exchange 同样回 Ack。要知道有没有路由到队列,要加 mandatory,此时先收到 Basic.Return 再收到 Ack。
x-max-length=3,连发 5 条的结果: ['ack', 'ack', 'ack', 'NackError', 'NackError'] 线上 broker 发出的确认帧: ['Basic.Ack tag=1 multiple=False', 'Basic.Ack tag=2 multiple=False', 'Basic.Ack tag=3 multiple=False', 'Basic.Nack tag=4 multiple=True requeue=True', 'Basic.Nack tag=5 multiple=True requeue=True'] 队列里最终有 3 条
队列满且 x-overflow=reject-publish 时,新发布得到 Basic.Nack,发布方明确知道被拒;没开 confirm 就不知道。