真实只在bootstrap.servers里配置一个broker地址,验证客户端能不能学到完整的集群元数据,并且真实把消息发送到配置里从未出现过的其它broker。
命令 · python p = Producer({"bootstrap.servers": "localhost:19094"}) # 真实只填了node2 c = Consumer({"bootstrap.servers": "localhost:19094", ...}) md = c.list_topics(timeout=5)
真实通过node2一个入口拿到的完整broker列表: broker id=1 localhost:19092 broker id=2 localhost:19094 broker id=3 localhost:19096 order-demo真实的分区->leader映射(全部来自这一次对node2的元数据请求): partition 0: leader=3, replicas=[3,1,2], isr=[1,2,3] partition 1: leader=2, replicas=[1,2,3], isr=[1,2,3] partition 2: leader=2, replicas=[2,3,1], isr=[1,2,3]
bootstrap.servers真实的作用只是"给客户端一个能连上去问路的起点",不是"客户端只能用这些broker"。真实连上node2之后,客户端发了一次元数据请求(Metadata API),node2真实把整个集群的broker列表、以及它自己知道的每个topic分区的leader/副本/ISR,一次性都返回了——这些信息node2自己也是从Controller那里学来的(参见《Kafka Controller 到底负责什么》),任何一个broker都能回答这个问题,不需要真实固定去问某一个特定节点。
命令 · python for i in range(6): p.produce("order-demo", key=f"probe-{i}".encode(), value=..., callback=cb) p.flush(10)
真实的投递结果(分区号=消息真正落到的分区): [('ok', 0), ('ok', 1), ('ok', 1), ('ok', 2), ('ok', 2), ('ok', 2)]每一步都是本文上面真实用confluent-kafka客户端跑出来的原始结果。
bootstrap.servers真实只是引导阶段用的联系人,不是请求真正的路由表。客户端第一次连接任意一个列出的broker后,会真实主动请求一份完整的集群元数据(哪些broker、哪些topic、每个分区的leader是谁),之后所有的produce/fetch请求,都会真实依据这份缓存的元数据直接连接到正确的目标broker,不需要经过最初连接的那一个中转。这也是为什么客户端配置里通常建议真实填多个broker地址——只是为了防止刚好第一个不可用,不代表元数据本身依赖某个固定的broker。