阅读《深入理解Kafka核心设计与实践原理》第四章 主题与分区 主题与分区

@

1. 主题的管理

主题的管理包括创建主题,查看主题信息,修改主题,删除主题。

1.1 创建主题

创建主题语句

bin/kafka-topics.sh --zookeeper localhost:2181/kafka --create --topic test --partitions 10 --replication-factor 3

上面是按照既定的内部逻辑来自动分配,如何进行手动分配分区。

手动分配分区命令入下:创建一个副本数为2,分区为4的topic

bin/kafka-topics.sh --zookeeper localhost:2181/kafka --create --topic test --replica-assignment 2:0,0:1,1:2,2:1 

注意:同一个分区内的副本不能有重复,比如0:0,1:1 会报错
当topic名有“.” 时,会将"." 替换为“_” 例如:“topic.name_1” 会变成 “topic_name_1”
主题命名不推荐使用"__",因为双下划线一般看作kafka的内部主题如 __consumer_offsets 和 __transaction_state

当创建一个主题时,实质是在Zookeeper中的/brokers/topics节点下创建与该主题对应的子节点并写入主题相关的配置信息

1.2 查看主题

查看当前所有可用主题:

bin/kafka-topics.sh --zookeeper localhost:2181/kafka -list

查看主题信息:

bin/kafka-topics.sh --zookeeper localhost:2181/kafka --describe --topic test

查看多个主题详细信息用“,”隔开:

bin/kafka-topics.sh --zookeeper localhost:2181/kafka --describe --topic test,test2

查找所有包含失效副本的分区:

bin/kafka-topics.sh --zookeeper localhost:2181/kafka --describe --topic test --under-replicated-partitions

查找主题中没有leader副本的分区:

bin/kafka-topics.sh --zookeeper localhost:2181/kafka --describe --topic test --unavailable-partitions

1.3 修改主题

当一个主题创建后,可以进行修改分区,修改配置等。

增加分区命令:

bin/kafka-topics.sh --zookeeper localhost:2181/kafka --alter --topic topic-config --partitions 3

上面这个命令会有告警信息,当主题中消息包含key时,根据key计算分区的行为会受到影响,原本发往分区0的消息会被发往分区1,2 对于基于key计算的主题而言,建议一开始设置好分区数量。

重点kafka只支持增加分区数而不支持减少分区数

为什么不支持减少分区?

考虑删除的分区中的消息如何处理,如果随着分区一起消息则消息的可靠性得不到保障,如果需要保留又要考虑如何保留,如果直接存储到现有分区尾部,消息的时间戳就不会递增,如果分散插入现有的分区,内部的数据复制会占用很大的资源

1.4 配置管理

kafka-configs.sh 对配置进行操作的。本质也是创建一个zookeeper节点,并将变更的配置写入这个节点。

查看主题topic-config的配置

bin/kafka-config.sh --zookeeper localhost:2181/kafka --describe --entity-type topics --entity-name test

--entity-type 和 --entity-name 的一一对应关系如下:

entity-type entity-name
topics 主题的名称
brokers brokerId的值
clients client.id的值
users 用户名

增加配置命令如下:

bin/kafka-config.sh --zookeeper localhost:2181/kafka --alter --entity-type topics --entity-name test --add-config cleanup.policy=compact,max.message.bytes=10000

1.5 删除主题

如果确定不用这个这个主题,最好的方式是将其删除,可以释放一些资源,如磁盘,文件描述符等,删除主题是一个不可逆的操作。

脚本删除主题本质也是在zookeeper中的/admin/delete_topics 路径下创建一个与topic同名的节点,真正的删除主题动作是由Kafka的控制器负责完成

删除命令如下:

bin/kafka-topics.sh --zookeeper localhost:2181/kafka --delete test --if-exists

也可以手动删除主题:

  1. 删除zookeeper节点 /config/topics/test
  2. 删除zookeeper节点/brokers/topics/test 及其子节点
  3. 删除集群中所有与主题test有关的文件 rm -rf /tmp/kafka-logs/test

2. 主题的管理

包括优先副本的选举,分区的重分配,复制限流,修改副本因子等。

2.1 优先副本选举

分区使用多副本保证消息的可靠性,只有leader副本对外提供读写服务,follower副本负责内部的消息同步

如果一个分区的leader副本不可用,那么就意味着整个分区变得不可用

当leader节点发生故障,选一个follower成为新的leader,恢复后重新假如集群,只能成为follower。

kafka引入优先副本,指AR集合中第一个副本,优先让他成为leader。

这样只能保证分区平衡,不能保证集群负载均衡

为什么不能保障集群负载均衡?
因为有些leader副本的负载很高,而有些leader副本的负载很低。也就是说leader分配均衡,分区分配均衡,也并不能保证整个集群的负载就是均衡的

怎么解决集群的负载均衡问题?
Kafka提供了分区自动平衡的功能,对应的参数auto.leader.rebalance.enable 此参数默认为true,会启动一个定时任务轮训所有leader节点,但是生产环境建议设置为false,因为会引起负面的性能问题(在重新平衡期间会发生业务阻塞、频繁超时等问题),另一个执行时间无法自主掌控。不如手动执行分区平衡

leader副本重新平衡脚本命令:

bin/kafka-preferred-replica-election.sh --zookeeper loaclhost:2181/kafka

但是上面这个命令会将整个集群上所有的分区都执行一遍优先副本选举操作,而且leader副本的转移也是一项高成本的工作

如何对部分分区执行优先副本操作?

利用 path-to-json-file 来指定一个json 参数来小批量对部分分区执行优先副本选举操作。

例如:对主题"topic-test"执行优先副本选举操作,创建一个json文件 文件名假定为election.json

{
	"partitions": [
		{
			"Partition":0,
			"topic":"topic-test"
		},
		{
			"Partition":1,
			"topic":"topic-test"
		},
		{
			"Partition":2,
			"topic":"topic-test"
		}
	]
}

然后通过kafka-preferred-replica-election.sh 脚本配合path-to-json-file参数来实现对主题“topic-test”的优选副本选举操作:

bin/kafka-preferred-replica-election.sh --zookeeper localhost:2181/kafka --path-to-json-file election.json

总结:生产环境中一般使用path-to-json-file参数来分批,手动执行优先副本选举操作,同时要注意避开业务高峰期。

2.2 分区重分配

集群上线或下线broker节点都会影响整个集群的负载均衡。

当集群的一个节点突然下线,那么这个节点上所有的分区都处于不可用状态,kafka并不会讲这些失效的分区副本自动迁移到集群中剩余的broker上,影响集群的负载均衡,可靠性,高可用。

当集群新增broker节点,只有新创建的主题分区副本才有可能被分配到这个节点,之前的主题并不会自动分配到新加入的节点,这也会造成负载不均衡问题

怎么解决这个问题?
Kafka提供了Kafka-reassign-partitions.sh脚本来对分区进行迁移

如何使用?
第一步:创建一个json文件(文件名假定为reassign.json),文件内容为要进行重分配的主题清单。对于主题“topic-test”而言,示例如下:

{
	"topics":[
	 	{
			"topic":"topic-reassign"
		}
	 ],
	 "version":1
}

第二步:根据这个json文件和指定所要分配的broker节点列表来生成一份候选的重分配方案。

bin/kafka-reassign-partitions.sh --zookeeper localhost:2181/kafka --generate --topics-to-move-json-file reassign.json --broker-list 0,2

这里会打印两个json格式的内容,第一个是对应的json内容当前的分区副本分配情况,记录起来方便回滚,第二个是重分配的候选方案,只是一个可行性方案,并没有真正执行。

我们需要将第二个json内容保存在一个json文件中,假定这个文件的名称为project.json

第三步执行具体的重分配动作

bin/kafka-reassign-partitions.sh --zookeeper localhost:2181/kafka --execute --reassignment-json-file project.json

第四步 查看分配进度

bin/kafka-reassign-partitions.sh --zookeeper localhost:2181/kafka --verify --reassignment-json-file project.json

为了防止服务器资源被过多占用,影响本来的业务 可以增加限流参数:

bin/kafka-reassign-partitions.sh --zookeeper localhost:2181/kafka --execute --reassignment-json-file project.json --throttle 1024

限流速度上限为1024B/s

总结:分区重分配的基本原理是通过控制器为每个分区添加新副本(增加副本因子),新的副本将从分区的leader副本哪里复制所有的数据,根据分区的不同,复制过程可能比较费时,因为数据都是通过网络复制到新副本上,复制完成后,控制器将旧副本从副本移除(恢复原先的副本因子)

2.3 修改副本因子

增加副本因子:

第一步:创建json文件,文件名假定为project.json,文件内容示例如下:

{
	"topic":"topic-test",
	"partition":1,
	"replicas":[
		2,
		1,
		0
	],
	"log_dirs":[
		"any",
		"any",
		"any"
	]
}

第二步: 执行kafka-reassign-partition.sh脚本

bin/kafka-reassign-partitions.sh --zookeeper loaclhost:2181/kafka --execute --reassignment-json-file project.json

减少副本因子的方法和上面一致,不过多介绍。

3. 如何选择合适的分区数

3.1 性能测试工具

生产者性能测试:

向一个只有1个分区和1个副本的主题发送100万条消息,每条消息大小1024B,生产者对应的acks = 1。

bin/kafka-producer-perf-test.sh --topic topic-1 --num-records 1000000 --record-size 1024 --throughput -1 --print-metrics --producer-props bootstrap.servers=localhost:9092 acks=1

消费者性能测试
简单消费主题topic-1中的100万条消息。

bin/kafka-consumer-perf-test.sh --topic topic-1 --messages 1000000 -broker-list localhost:9092

3.2 分区数的设定

注意分区并不是越多越好,更多还是会和磁盘,文件系统,I/O调度策略有关,另外会带来文件描述符的开销,一个分区一个文件句柄,而且如果集群中某个broker宕机,那么大量的分区需要进行leader切换,并且这个时间窗口内的这些分区也会变得不可用。