Kafka自定义分区

Kafka中,topic是逻辑上的概念,而partition是物理上的概念。不用担心,这些对用户来说是透明的。生产者(producer)只关心自己将消息发布到哪个topic,而消费者(consumer)只关心自己订阅了哪个topic上的消息,至少topic上的消息分布在哪些partition节点上,它本身并不关心。

如果没有分区的概念,那么topic的消息集合将集中于某一台服务器上,单节点的存储性能马上将成为瓶颈,当访问该topic存取数据时,吞吐也将成为瓶颈。

介于此,kafka的设计方案是,生产者在生产数据的时候,可以为每条消息人为的指定key,这样消息被发送到broker时,会根据分区规则,选择消息将被存储到哪一个分区中。

如果分区规则设置合理,那么所有的消息将会被均匀/线性的分布到不同的分区中,这样就实现了负载均衡和水平扩展。另外,在消费者端,同一个消费组可以多线程并发的从多个分区中 同时消费数据。 上述分区规则,实际上是实现了kafka.producer.Partitioner接口的一个类,这个实现类可以根据自己的业务规则进行自定义制定,如根据hash算法指定分区的分布规则。 如以下这个类,我们先获取key的hashcode值,再跟分区数量(配置文件中为numPartitions)做模运算,结果值作为分区存储位置,这样可以实现数据均匀线性的分布。

 


①自定义TxcPartitioner 类
public class TxcPartitioner implements Partitioner{

    @Override
    public void configure(Map<String, ?> arg0) {
        
    }

    @Override
    public void close() {
        
    }

    @Override
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
        int size = partitions.size();
    //如果消息的 key 为 null,默认分配到指定分区
if(keyBytes == null) { return 0; }
     //
如果 key 不为 null,并且使用了默认的分区器,kafka 会使用自己的 hash 算法对 key 取 hash 值,
//使用 hash 值与 partition 数量取模,从而确定发送到哪个分区。
//注意:此时 key 相同的消息会发送到相同的分区(只要 partition 的数量不变化)
    return Utils.toPositive(Utils.murmur2(keyBytes)) % size; 
}

②发送消息的方法如下

public void send(String topic,String key,RequestMessage message){  
        try {  
            if(kafkaProducer != null) {
                ProducerRecord<String, String> record = new ProducerRecord<String, String>(topic,key,JSONObject.toJSONString(message));  
                Future<RecordMetadata> future = kafkaProducer.send(record);
                RecordMetadata metadata = future.get();
                if(metadata != null) {
                    sysLog.debug("【Kafka message send success,topic = {}, partition is {} 】 " , metadata.topic(),metadata.partition());
                }else {
                    sysLog.error("【Kafka message send fail 】");
                    throw new KafkaSendException("Kafka message send fail");
                }
            }else {
                sysLog.error("【TxcProducer is not init】");
                throw new KafkaInitException("TxcProducer is not init");
            }
           
        }catch(Exception e){ 
            sysLog.error("【Kafka message send fail , exception = {}】 ",ExceptionUtil.collectExceptionStackMsg(e));
            throw new KafkaSendException("Kafka message send fail");  
        }  
    }  

③生产者配置中添加配置

//设置自定义分区
properties.put(TxcParameType.partitioner_class.getName(), TxcPartitioner.class.getName());



猜你喜欢

转载自www.cnblogs.com/cowboys/p/9154763.html