topic创建详解

1、自动创建

如果kafka broker中的config/server.properties配置文件中配置了auto.create.topics.enable参数为true(默认值就是true),

那么当生产者向一个尚未创建的topic发送消息时,会自动创建一个num.partitions(默认值为1)个分区和default.replication.factor(默认值为1)个副本的对应topic。

不过一般不建议将auto.create.topics.enable参数设置为true,因为这个参数会影响topic的管理与维护。

2、手动创建 


kafka提供的kafka-topics.sh脚本来创建,并且我们也建议通过这种方式(或者相关的变种方式)来创建topic。

kafka-topics.sh代码:

exec $(dirname $0)/kafka-run-class.sh kafka.admin.TopicCommand "$@"
这个脚本的主要作用就是运行kafka.admin.TopicCommand。在main方法中判断参数列表中是否包含有"create“,如果有,那么就实施创建topic的任务。创建topic时除了需要zookeeper的地址参数外,还需要指定topic的名称、副本因子replication-factor以及分区个数partitions等必选参数 ,还可以包括disable-rack-aware、config、if-not-exists等可选参数。
真正的创建过程是由createTopic这个方法中执行的

def createTopic(zkUtils: ZkUtils, opts: TopicCommandOptions) {
  //获取topic参数所对应的值,也就是topic名称——topic-test
  val topic = opts.options.valueOf(opts.topicOpt)
  //将参数解析成Properties参数,config所指定的参数集
  val configs = parseTopicConfigsToBeAdded(opts)
  //对应if-not-exists
  val ifNotExists = opts.options.has(opts.ifNotExistsOpt)
  if (Topic.hasCollisionChars(topic))
    println("WARNING: Due to limitations in metric names, topics with a period ('.') or underscore ('_') could collide. To avoid issues it is best to use either, but not both.")
  try {
    //检测是否有replica-assignment参数
    if (opts.options.has(opts.replicaAssignmentOpt)) {
      val assignment = parseReplicaAssignment(opts.options.valueOf(opts.replicaAssignmentOpt))
      AdminUtils.createOrUpdateTopicPartitionAssignmentPathInZK(zkUtils, topic, assignment, configs, update = false)
    } else {
      //验证一下执行kafka-topics.sh时参数列表中是否包含有partitions和replication-factor这两个参数
      CommandLineUtils.checkRequiredArgs(opts.parser, opts.options, opts.partitionsOpt, opts.replicationFactorOpt)
      //获取paritions、replication-factor参数所对应的值以及验证是否包含disable-rack-aware这个参数
      val partitions = opts.options.valueOf(opts.partitionsOpt).intValue
      val replicas = opts.options.valueOf(opts.replicationFactorOpt).intValue
      //broker所在机架信息
      val rackAwareMode = if (opts.options.has(opts.disableRackAware)) RackAwareMode.Disabled
                          else RackAwareMode.Enforced
      AdminUtils.createTopic(zkUtils, topic, partitions, replicas, configs, rackAwareMode)
    }
    println("Created topic \"%s\".".format(topic))
  } catch  {
    case e: TopicExistsException => if (!ifNotExists) throw e
  }
}

createTopic方法中

(1)首先获取topic的名称,config参数集以及判断是否有if-not-exists参数。config参数集可以用来设置topic级别的配置以覆盖默认配置。

如果创建的topic再现有的集群中存在,那么会报出异常:TopicExistsException,

如果创建的时候带了if-not-exists参数,那么发现topic冲突的时候可以不做任何处理;如果topic不冲突,那么和不带if-not-exists参数的行为一样正常topic,不带有if-not-exists参数和带有if-not-exists参数的效果如下:

[root@node1 kafka_2.12-1.0.0]# bin/kafka-topics.sh --create --zookeeper 192.168.0.2:2181/kafka100 --topic topic-test1 --replication-factor 2 --partitions 4
Error while executing topic command : Topic 'topic-test1' already exists.
[2018-01-30 17:52:32,425] ERROR org.apache.kafka.common.errors.TopicExistsException: Topic 'topic-test1' already exists.
 (kafka.admin.TopicCommand$)
[root@node1 kafka_2.12-1.0.0]# bin/kafka-topics.sh --create --zookeeper 192.168.0.2:2181/kafka100 --topic topic-test1 --replication-factor 2 --partitions 4 --if-not-exists
[root@node1 kafka_2.12-1.0.0]# 


(2)检测topic名称中是否包含有“.”或者“_”字符的,这一个步骤在AdminUtils.createOrUpdateTopicPartitionAssignmentPathInZK()中调用validateCreateOrUpdateTopic()方法实现的。为什么要检测这两个字符呢?因为在Kafka的内部做埋点时会根据topic的名称来命名metrics的名称,并且会将句点号“.”改成下划线"_"。假设遇到一个topic的名称为“topic.1_2”,还有一个topic的名称为“topic_1.2”,那么最后的metrics的名称都为“topic_1_2”,所以就会发生名称冲突。举例如下,首先创建一个以"topic.1_2"名称的topic,提示WARNING警告,之后在创建一个“topic.1_2”时发生InvalidTopicException异常。
[root@node2 kafka_2.12-1.0.0]# bin/kafka-topics.sh --create --zookeeper 192.168.0.2:2181/kafka100 --topic topic.1_2 --replication-factor 2 --partitions 4
WARNING: Due to limitations in metric names, topics with a period ('.') or underscore ('_') could collide. To avoid issues it is best to use either, but not both.
Created topic "topic.1_2".
[root@node2 kafka_2.12-1.0.0]# bin/kafka-topics.sh --create --zookeeper 192.168.0.2:2181/kafka100 --topic topic_1.2 --replication-factor 2 --partitions 4
WARNING: Due to limitations in metric names, topics with a period ('.') or underscore ('_') could collide. To avoid issues it is best to use either, but not both.
Error while executing topic command : Topic 'topic_1.2' collides with existing topics: topic.1_2
[2018-01-31 20:27:28,449] ERROR org.apache.kafka.common.errors.InvalidTopicException: Topic 'topic_1.2' collides with existing topics: topic.1_2
 (kafka.admin.TopicCommand$)

注意:
topic的命名同样不推荐(虽然可以这样做)使用双下划线“__”开头,因为以双下划线开头的topic一般看作是kafka的内部topic,比如__consumer_offsets和__transaction_state。topic的名称必须由大小写字母、数字、“.”、“-”、“_”组成,不能为空、不能为“.”、不能为“…”,且长度不能超过249。

(3)检测出有replica-assignment参数,那么就是制定了副本的分配方式

[root@node1 kafka_2.12-1.0.0]# bin/kafka-topics.sh --describe --zookeeper 192.168.0.2:2181/kafka100 --topic topic-test1
Topic:topic-test1    PartitionCount:4    ReplicationFactor:2    Configs:
    Topic: topic-test    Partition: 0    Leader: 0    Replicas: 0,1    Isr: 0,1
    Topic: topic-test    Partition: 1    Leader: 1    Replicas: 1,0    Isr: 1,0
    Topic: topic-test    Partition: 2    Leader: 0    Replicas: 0,1    Isr: 0,1
    Topic: topic-test    Partition: 3    Leader: 1    Replicas: 1,0    Isr: 1,0
[root@node1 kafka_2.12-1.0.0]# bin/kafka-topics.sh --create --zookeeper 192.168.0.2:2181/kafka100 --topic topic-test1 --replication-factor 2 --partitions 4 --if-not-exists
[root@node1 kafka_2.12-1.0.0]# bin/kafka-topics.sh --describe --zookeeper 192.168.0.2:2181/kafka100 --topic topic-test1
Topic:topic-test    PartitionCount:4    ReplicationFactor:2    Configs:
    Topic: topic-test    Partition: 0    Leader: 0    Replicas: 0,1    Isr: 0,1
    Topic: topic-test    Partition: 1    Leader: 1    Replicas: 1,0    Isr: 1,0
    Topic: topic-test    Partition: 2    Leader: 0    Replicas: 0,1    Isr: 0,1
    Topic: topic-test    Partition: 3    Leader: 1    Replicas: 1,0    Isr: 1,0
[root@node1 kafka_2.12-1.0.0]# bin/kafka-topics.sh --create --zookeeper 192.168.0.2:2181/kafka100 --topic topic-test2 --replica-assignment 0:1,1:0,0:1,1:0
Created topic "topic-test2".
[root@node1 kafka_2.12-1.0.0]# bin/kafka-topics.sh --describe --zookeeper 192.168.0.2:2181/kafka100 --topic topic-test2
Topic:topic-test2    PartitionCount:4    ReplicationFactor:2    Configs:
    Topic: topic-test2    Partition: 0    Leader: 0    Replicas: 0,1    Isr: 0,1
    Topic: topic-test2    Partition: 1    Leader: 1    Replicas: 1,0    Isr: 1,0
    Topic: topic-test2    Partition: 2    Leader: 0    Replicas: 0,1    Isr: 0,1
    Topic: topic-test2    Partition: 3    Leader: 1    Replicas: 1,0    Isr: 1,0

手动指定“–replica-assignment 0:1,1:0,0:1,1:0”后副本的分配方式和自动分配的效果一样,createTopic方法中如果判断opts.options.has(opts.replicaAssignmentOpt)满足条件,那么接下去的工作就是解析并验证指定的副本是否有重复、每个分区的副本个数是否相同等等。

1)指定0:0,1:1这种(副本重复)就会报出AdminCommandFailedException异常

2)指定0:1, 0, 1:0这种(分区副本个数不同)就会报出AdminOperationException异常

3)指定0:1,0:1,1:0这种企图跳过一个partition连续序号的行为也是不被允许的,就会报NumberFormatException异常

(4)没有指定replica-assignment参数,采用kafka默认的分区副本分配策略来创建topic

从0.10.x版本开始,kafka可以支持指定broker的机架信息,如果指定了机架信息则在副本分配时会尽可能地让分区的副本分不到不同的机架上。指定机架信息是通过kafka的配置文件config/server.properties中的broker.rack参数来配置的,比如配置当前broker所在的机架为“RACK1”:

broker.rack=RACK1

AdminUtils.createTopic的方法:

def createTopic(zkUtils: ZkUtils,
                topic: String,
                partitions: Int,
                replicationFactor: Int,
                topicConfig: Properties = new Properties,
                rackAwareMode: RackAwareMode = RackAwareMode.Enforced) {
  val brokerMetadatas = getBrokerMetadatas(zkUtils, rackAwareMode)
  val replicaAssignment = AdminUtils.assignReplicasToBrokers(brokerMetadatas, partitions, replicationFactor)
  AdminUtils.createOrUpdateTopicPartitionAssignmentPathInZK(zkUtils, topic, replicaAssignment, topicConfig)
}

获取集群中每个broker的brokerId和机架信息(Option[String]类型)信息的列表,为下面的 AdminUtils.assignReplicasToBrokers()方法做分区副本分配前的准备工作。

AdminUtils.assignReplicasToBrokers()首先是做一些简单的验证工作:分区个数partitions不能小于等于0、副本个数replicationFactor不能小于等于0以及副本个数replicationFactor不能大于broker的节点个数,其后的步骤就是方法最重要的两大核心:assignReplicasToBrokersRackUnaware和assignReplicasToBrokersRackAware,看这个名字也应该猜出个一二来,前者用来针对不指定机架信息的情况,而后者是用来针对指定机架信息的情况,后者更加复杂一点。
assignReplicasToBrokersRackUnaware和assignReplicasToBrokersRackAware  具体原理参考博客

 

(5)zookeeper中创建/brokers/topics/topic-test持久化节点,对应节点的数据就是以json格式呈现的分区分配的结果集,格式参考:{“version”:1,“partitions”:{“2”:[0,1],“1”:[1,0],“3”:[1,0],“0”:[0,1]}}。如果配置了config参数的话,同样先进行验证,如若无误就创建/config/topics/topic-test节点,并写入config对应的数据,格式参考:{“version”:1,“config”:{“max.message.bytes”:“1000013”}}
 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Integration MQTT 提供了一个方便的方式来集成 MQTT 客户端到 Spring 应用程序中。下面是一个示例代码,演示如何在 Spring Integration 中使用 MQTT 子协议实现多个主题的订阅。 首先,需要在 Spring 配置文件中声明 MQTT 连接工厂和 MQTT 输入通道: ```xml <bean id="mqttClientFactory" class="org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory"> <property name="userName" value="${mqtt.username}" /> <property name="password" value="${mqtt.password}" /> </bean> <int-mqtt:message-driven-channel-adapter id="mqttInbound" client-factory="mqttClientFactory" auto-startup="true" url="${mqtt.url}" topics="topic1,topic2,topic3" qos="2" converter="mqttDefaultPahoMessageConverter" channel="mqttInputChannel"/> ``` 在这个示例中,我们使用 `DefaultMqttPahoClientFactory` 来创建 MQTT 连接工厂,`mqtt.username` 和 `mqtt.password` 属性用于设置连接的用户名和密码。`mqttInbound` 是一个消息驱动的通道适配器,用于从 MQTT 代理服务器接收消息。`mqtt.url` 属性设置了 MQTT 服务器的 URL,`topics` 属性设置了要订阅的主题列表,`qos` 属性设置了消息的服务质量,`converter` 属性设置了消息转换器,`mqttInputChannel` 是一个输入通道,用于接收 MQTT 消息。 接下来,可以在 Spring 配置文件中声明一个消息处理器,用于处理接收到的 MQTT 消息: ```xml <int:service-activator input-channel="mqttInputChannel" ref="mqttMessageHandler" method="handleMessage"/> <bean id="mqttMessageHandler" class="com.example.MqttMessageHandler"/> ``` 在这个示例中,我们使用 `service-activator` 元素来声明一个消息处理器,`mqttInputChannel` 是输入通道,`mqttMessageHandler` 是消息处理器的引用,`handleMessage` 方法用于处理接收到的消息。`MqttMessageHandler` 是一个自定义的消息处理器,它实现了 `MessageHandler` 接口: ```java public class MqttMessageHandler implements MessageHandler { @Override public void handleMessage(Message<?> message) throws MessagingException { // 处理接收到的消息 } } ``` 在 `handleMessage` 方法中,可以获取到接收到的 MQTT 消息,然后进行处理。 总之,以上是一个简单的示例,演示了如何在 Spring Integration 中使用 MQTT 子协议实现多个主题的订阅。需要注意的是,要在 Spring 配置文件中导入 `int-mqtt` 命名空间和 `int` 命名空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值