Kafka分区机制介绍与示例 - Go语言中文社区

Kafka分区机制介绍与示例


原文链接:http://lxw1234.com/archives/2015/10/538.htm


Kafka中可以将Topic从物理上划分成一个或多个分区(Partition),每个分区在物理上对应一个文件夹,以”topicName_partitionIndex”的命名方式命名,该文件夹下存储这个分区的所有消息(.log)和索引文件(.index),这使得Kafka的吞吐率可以水平扩展。

生产者在生产数据的时候,可以为每条消息指定Key,这样消息被发送到broker时,会根据分区规则选择被存储到哪一个分区中,如果分区规则设置的合理,那么所有的消息将会被均匀的分布到不同的分区中,这样就实现了负载均衡和水平扩展。另外,在消费者端,同一个消费组可以多线程并发的从多个分区中同时消费数据(后续将介绍这块)。

上面所说的分区规则,是实现了kafka.producer.Partitioner接口的类,可以自定义。比如,下面的代码SimplePartitioner中,将消息的key做了hashcode,然后和分区数(numPartitions)做模运算,使得每一个key都可以分布到一个分区中:

  1. package com.lxw1234.kafka;
  2.  
  3. import kafka.producer.Partitioner;
  4. import kafka.utils.VerifiableProperties;
  5.  
  6. public class SimplePartitioner implements Partitioner {
  7. public SimplePartitioner (VerifiableProperties props) {
  8. }
  9. @Override
  10. public int partition(Object key, int numPartitions) {
  11. int partition = 0;
  12. String k = (String)key;
  13. partition = Math.abs(k.hashCode()) % numPartitions;
  14. return partition;
  15. }
  16. }

在创建Topic时候可以使用–partitions <numPartitions>指定分区数。也可以在server.properties配置文件中配置参数num.partitions来指定默认的分区数。

但有一点需要注意,为Topic创建分区时,分区数最好是broker数量的整数倍,这样才能是一个Topic的分区均匀的分布在整个Kafka集群中,假设我的Kafka集群由4个broker组成,以下图为例:

kafka partition

创建带分区的Topic

现在创建一个topic “lxw1234”,为该topic指定4个分区,那么这4个分区将会在每个broker上各分布一个:

  1. ./kafka-topics.sh
  2. --create
  3. --zookeeper zk1:2181,zk2:2181,zk3:2181
  4. --replication-factor 1
  5. --partitions 4
  6. --topic lxw1234

kafka partition

这样所有的分区就均匀分布在集群中,如果创建topic时候指定了3个分区,那么就有一个broker上没有该topic的分区。

带分区规则的生产者

现在用一个生产者示例(PartitionerProducer),向Topic lxw1234中发送消息。该生产者使用的分区规则,就是上面的SimplePartitioner。从0-10一共11条消息,每条消息的key为”key”+index,消息内容为”key”+index+”–value”+index。比如:key0–value0、key1–value1、、、key10–value10。

  1. package com.lxw1234.kafka;
  2.  
  3. import java.util.Properties;
  4.  
  5. import kafka.javaapi.producer.Producer;
  6. import kafka.producer.KeyedMessage;
  7. import kafka.producer.ProducerConfig;
  8.  
  9. public class PartitionerProducer {
  10. public static void main(String[] args) {
  11. Properties props = new Properties();
  12. props.put("serializer.class", "kafka.serializer.StringEncoder");
  13. props.put("metadata.broker.list", "127.0.0.17:9091,127.0.0.17:9092,127.0.0.102:9091,127.0.0.102:9092");
  14. props.put("partitioner.class", "com.lxw1234.kafka.SimplePartitioner");
  15. Producer<String, String> producer = new Producer<String, String>(new ProducerConfig(props));
  16. String topic = "lxw1234";
  17. for(int i=0; i<=10; i++) {
  18. String k = "key" + i;
  19. String v = k + "--value" + i;
  20. producer.send(new KeyedMessage<String, String>(topic,k,v));
  21. }
  22. producer.close();
  23. }
  24. }
  25.  

理论上来说,生产者在发送消息的时候,会按照SimplePartitioner的规则,将key0做hashcode,然后和分区数(4)做模运算,得到分区索引:

hashcode(”key0”) % 4 = 1

hashcode(”key1”) % 4 = 2

hashcode(”key2”) % 4 = 3

hashcode(”key3”) % 4 = 0

         ……

对应的消息将会被发送至相应的分区中。

统计各分区消息的消费者

下面的消费者代码用来验证,在消费数据时,打印出消息所在的分区及消息内容:

  1. package com.lxw1234.kafka;
  2.  
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.Properties;
  7.  
  8. import kafka.consumer.Consumer;
  9. import kafka.consumer.ConsumerConfig;
  10. import kafka.consumer.ConsumerIterator;
  11. import kafka.consumer.KafkaStream;
  12. import kafka.javaapi.consumer.ConsumerConnector;
  13. import kafka.message.MessageAndMetadata;
  14.  
  15. public class MyConsumer {
  16. public static void main(String[] args) {
  17. String topic = "lxw1234";
  18. ConsumerConnector consumer = Consumer.createJavaConsumerConnector(createConsumerConfig());
  19. Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
  20. topicCountMap.put(topic, new Integer(1));
  21. Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = consumer.createMessageStreams(topicCountMap);
  22. KafkaStream<byte[], byte[]> stream = consumerMap.get(topic).get(0);
  23. ConsumerIterator<byte[], byte[]> it = stream.iterator();
  24. while(it.hasNext()) {
  25. MessageAndMetadata<byte[], byte[]> mam = it.next();
  26. System.out.println("consume: Partition [" + mam.partition() + "] Message: [" + new String(mam.message()) + "] ..");
  27. }
  28. }
  29. private static ConsumerConfig createConsumerConfig() {
  30. Properties props = new Properties();
  31. props.put("group.id","group1");
  32. props.put("zookeeper.connect","127.0.0.132:2181,127.0.0.133:2182,127.0.0.134:2183");
  33. props.put("zookeeper.session.timeout.ms", "400");
  34. props.put("zookeeper.sync.time.ms", "200");
  35. props.put("auto.commit.interval.ms", "1000");
  36. props.put("auto.offset.reset", "smallest");
  37. return new ConsumerConfig(props);
  38. }
  39. }
  40.  
  41.  

运行程序验证结果

先启动消费者,再运行生产者。

之后在消费者的控制台可以看到如下输出:

kafka partition

结果和正常预期一致。


版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/liuxiangke0210/article/details/71406982
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-06 23:11:20
  • 阅读 ( 888 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢