默认分区器只认哈希值,不认业务含义——想让特定一类消息独占某个分区(比如给高优先级订单单独留一个分区,方便下游优先处理),光靠 key 哈希做不到,因为哈希结果落在哪个分区是不可控的。这次写了一个自定义 Partitioner,跟默认分区器的行为做对比。
命令 · 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 个不同分区,且和普通订单共享分区,没有任何隔离。
命令 · 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 是可行、代价也不大的方案——核心逻辑就是实现一个接口方法。
下面每一条的分区结果,都是本文上两节运行 Java 客户端得到的原始输出,这里逐条对比回放。