zoukankan      html  css  js  c++  java
  • Kafka-分区

    Kafka-分区

    kafka的消息时一个个键值对,ProducerRecord对象可以包含目标主题和值,键可以设置为默认的null,不过大多数应用程序会用到键。

    键有两个用途;可以作为消息的附加信息,也可以用来决定消息该被写到主题的哪个分区。拥有相同键的消息将被写到同一个分区。

    如果键值为null,并且使用了默认的分区器,那么记录将被随机的发送到主题内各个可用的分区上。分区器使用轮询(Round Robin)算法将消息均衡地分不到各个分区上。

    如果键不为空,并且使用了默认的分区器,那么kafka会对键进行散列(kafka自己的散列算法),然后根据散列值把消息映射到特定的分区上。同一个键总是被映射到同一个分区上,所以在进行映射时,我们会使用主题所有的分区,而不仅仅是可用的分区。所以,如果写入数据的分区是不可用的,那么就会发生错误。

    如果要使用键来映射分区,那么最好在创建主题的时候就把分区规划好,而且永远不要增加新分区。

    实现自定义分区策略

    除了散列分区之外,有时候也需要对数据进行不一样的分区。

    假设你是一个B2B供应商,有一个大客户,它是手持设备Apple的制造商。Apple占据了整体业务的10%的份额。如果使用默认的散列分区算法,Apple的账号记录将和其他账号记录一起被分配给相同的分区,导致这个分区比其它分区要大一些。服务器可能因此出现存储空间不足、处理缓慢等问题。我们需要给Apple分配单独的分区,然后使用散列分区算法处理其他账号。

     代码如下

    import org.apache.kafka.clients.producer.Partitioner;
    import org.apache.kafka.common.Cluster;
    import org.apache.kafka.common.InvalidRecordException;
    import org.apache.kafka.common.PartitionInfo;
    import org.apache.kafka.common.utils.Utils;
    
    import java.util.List;
    import java.util.Map;
    
    /**
     * @Author FengZhen
     * @Date 2020-03-31 22:38
     * @Description 自定义分区
     */
    public class ApplePartitioner implements Partitioner {
    
        @Override
        public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
            List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
            int numPartitions = partitions.size();
            if (null == keyBytes || !(key instanceof String)) {
                throw  new InvalidRecordException("We expect all messages to have customer name as key");
            }
    
            if (key.equals("Apple")){
                //分配到最后一个分区
                return numPartitions - 1;
            }
            return Math.abs(Utils.murmur2(keyBytes)) % (numPartitions - 1);
        }
    
        @Override
        public void close() {
    
        }
    
        @Override
        public void configure(Map<String, ?> map) {
    
        }
    }

    测试

    import org.apache.kafka.clients.producer.Callback;
    import org.apache.kafka.clients.producer.KafkaProducer;
    import org.apache.kafka.clients.producer.ProducerRecord;
    import org.apache.kafka.clients.producer.RecordMetadata;
    
    import java.util.Properties;
    
    /**
     * @Author FengZhen
     * @Date 2020-03-29 12:21
     * @Description kafka生产者使用
     */
    public class KafkaProducerTest {
    
        private static Properties kafkaProps = new Properties();
        static {
            kafkaProps.put("bootstrap.servers", "localhost:9092");
            kafkaProps.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
            kafkaProps.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        }
    
        public static void main(String[] args) {
            KafkaProducer<String, String> producer = new KafkaProducer(kafkaProps);
            ProducerRecord<String, String> record = new ProducerRecord<>("test","message_key","message_value");
    //        simpleSend(producer, record);
    //        sync(producer, record);
    //        aync(producer, record);
            udfPartition();
        }
    
        /**
         * 使用自定义分区
         * ./kafka-topics.sh --create --zookeeper localhost:2181/kafka_2_4_1 --replication-factor 1 --partitions 3 --topic test_partition
         */
        public static void udfPartition(){
            kafkaProps.put("partitioner.class", "com.chinaventure.kafka.partition.ApplePartitioner");
            KafkaProducer<String, String> producer = new KafkaProducer(kafkaProps);
            for (int i = 0; i < 10; i++){
                ProducerRecord<String, String> record = new ProducerRecord<>("test_partition",i % 3 == 0 ? "Apple": "Banana"+i,"我是" + i);
                producer.send(record, new DemonProducerCallback());
            }
            while (true){
                try {
                    Thread.sleep(10 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 最简单的方式发送,不管消息是否正常到达
         * @param producer
         */
        public static void simpleSend(KafkaProducer producer, ProducerRecord record){
            try {
                producer.send(record);
            } catch(Exception e){
                e.printStackTrace();
            }
        }
    
        /**
         * 同步发送
         * @param producer
         * @param record
         */
        public static void sync(KafkaProducer producer, ProducerRecord record){
            try {
                RecordMetadata recordMetadata = (RecordMetadata) producer.send(record).get();
                System.out.println("topic:" + recordMetadata.topic());
                System.out.println("partition:" + recordMetadata.partition());
                System.out.println("offset:" + recordMetadata.offset());
                System.out.println("metaData:" + recordMetadata.toString());
            } catch(Exception e){
                e.printStackTrace();
            }
        }
    
        /**
         * 异步发送
         * @param producer
         * @param record
         */
        public static void aync(KafkaProducer producer, ProducerRecord record){
            try {
                producer.send(record, new DemonProducerCallback());
                while (true){
                    Thread.sleep(10 * 1000);
                }
            } catch(Exception e){
                e.printStackTrace();
            }
        }
    
        private static class DemonProducerCallback implements Callback {
            @Override
            public void onCompletion(RecordMetadata recordMetadata, Exception e) {
                if (null != e){
                    e.printStackTrace();
                }else{
                    System.out.println("topic:" + recordMetadata.topic());
                    System.out.println("partition:" + recordMetadata.partition());
                    System.out.println("offset:" + recordMetadata.offset());
                    System.out.println("metaData:" + recordMetadata.toString());
                }
    
            }
        }
    }

    查看最后一个分区的日志

    FengZhendeMacBook-Pro:bin FengZhen$ cat /tmp/kafka-logs/test_partition-2/00000000000000000000.log 
    }??k?q1?q1???????????????$
    Apple我是0$&
    Apple我是3$&
    Apple我是6$&
    Apple我是9
  • 相关阅读:
    WPF进阶技巧和实战03-控件(3-文本控件及列表控件)
    WPF进阶技巧和实战08-依赖属性与绑定03
    WPF进阶技巧和实战08-依赖属性与绑定02
    WPF进阶技巧和实战08-依赖属性与绑定01
    WPF进阶技巧和实战07--自定义元素02
    WPF进阶技巧和实战07--自定义元素01
    Codeforces Round #730 Div. 2
    Codeforces Round #701 Div. 2
    Codeforces Round #700 Div. 2
    Codeforces Round #699 Div. 2
  • 原文地址:https://www.cnblogs.com/EnzoDin/p/12609557.html
Copyright © 2011-2022 走看看