bigywater.tech
Kafka 内部原理 · 04

分区器:默认分区、Key 分区和自定义分区

默认分区器只认哈希值,不认业务含义——想让特定一类消息独占某个分区(比如给高优先级订单单独留一个分区,方便下游优先处理),光靠 key 哈希做不到,因为哈希结果落在哪个分区是不可控的。这次写了一个自定义 Partitioner,跟默认分区器的行为做对比。

验证:默认分区器不会管 key 的业务含义,同类 key 可能分散到不同分区
发送 3 个 "vip-" 开头的 key 和 4 个 "order-" 开头的 key,默认分区器只按每个 key
验证:自定义 Partitioner 把 vip 消息固定隔离到指定分区
同样的 7 个 key,换上自定义的 VipPartitioner 后,3 个 vip key 全部落在分区 0

验证:默认分区器不会管 key 的业务含义,同类 key 可能分散到不同分区

命令 · java // 默认分区器,不做任何自定义配置 String[] keys = {"vip-1","order-100","vip-2","order-200","vip-3","order-300","order-400"}; for (String k : keys) { RecordMetadata md = producer.send(new ProducerRecord<>("custom-partitioner-demo2", k, "val")).get(); System.out.println("key=" + k + " partition=" + md.partition()); }
真实实测 · 本机执行输出
key=vip-1     partition=1
key=order-100 partition=1
key=vip-2     partition=0
key=order-200 partition=3
key=vip-3     partition=0
key=order-300 partition=0
key=order-400 partition=2

发送 3 个 "vip-" 开头的 key 和 4 个 "order-" 开头的 key,默认分区器只按每个 key 字符串的哈希值算分区,结果是 vip-1 落在分区 1,vip-2/vip-3 落在分区 0,跟 order-300 混在同一个分区——3 个 vip key 分散在 2 个不同分区,且和普通订单共享分区,没有任何隔离。

验证:自定义 Partitioner 把 vip 消息固定隔离到指定分区

命令 · java public class VipPartitioner implements Partitioner { public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) { String k = (String) key; if (k != null && k.startsWith("vip-")) return 0; // 固定路由到分区0 int numPartitions = cluster.partitionCountForTopic(topic); return 1 + (Math.abs(k.hashCode()) % (numPartitions - 1)); // 其它 key 分散到 1..N-1 } } // producer 配置: ProducerConfig.PARTITIONER_CLASS_CONFIG = "VipPartitioner"
真实实测 · 本机执行输出
key=vip-1     partition=0
key=order-100 partition=3
key=vip-2     partition=0
key=order-200 partition=2
key=vip-3     partition=0
key=order-300 partition=1
key=order-400 partition=3

同样的 7 个 key,换上自定义的 VipPartitioner 后,3 个 vip key 全部落在分区 0,一个不漏;4 个普通订单 key 分散到分区 1/2/3,没有一个落进分区 0。分区器的 partition() 方法拿到的是的 key、value 和集群元数据(Cluster 对象,可以查的分区数量),完全可以按业务逻辑而不是单纯哈希值来决定路由——这是默认分区器做不到的。

结论

默认分区器解决的是"同一个 key 有序",不解决"同一类业务消息独占资源"这个问题;对比证明了两者的差距:默认分区器下 3 个 vip key 分散在 2 个分区且和普通消息混居,自定义分区器下 3 个 vip key 精确、一致地隔离到了同一个分区。需要按业务规则路由(隔离、限流、灰度)时,自定义 Partitioner 是可行、代价也不大的方案——核心逻辑就是实现一个接口方法。

分步演示:vip 消息是怎么被隔离到同一个分区的

下面每一条的分区结果,都是本文上两节运行 Java 客户端得到的原始输出,这里逐条对比回放。

同一批 key,默认分区器和自定义分区器给出完全不同的路由结果
刚发送的这一条

    参考与说明

    • 本文两组对比数据都来自的 Java KafkaProducer 客户端,连接 3 节点集群,custom-partitioner-demo(自定义分区器)和 custom-partitioner-demo2(默认分区器)是两个创建的独立 4 分区 topic,用同一组 7 个 key 发送,保证对比公平。
    • 和《Producer 一条消息是怎么找到目标 Partition 的》一起看:那篇讲了无 key 时粘性分区器的批次行为,这篇讲了有 key 时如何用自定义逻辑覆盖默认的哈希路由,两者是"没有业务规则时" vs "有业务规则时"的两种路径。
    • 没有涉及:自定义分区器和 min.insync.replicas、分区数量变化(增加分区后哈希结果会变化,但显式业务规则不受影响)之间的相互影响,这个留给后面专门讨论"扩分区对已有路由的冲击"时再验证。
    京ICP备2026067502号-1京公网安备11010502063487号