消息系统Kafka
概述
在大数据中,使用了大量的数据。 关于数据,有两个主要挑战。第一个挑战是如何收集大量的数据,第二个挑战是分析收集的数据。 为了克服这些挑战,需要一个消息系统。
Apache Kafka 是一个分布式发布 - 订阅消息系统和一个强大的队列,可以处理大量的数据,并使您能够将消息从一个端点传递到另一个端点。 Kafka 适合离线和在线消息消费。 Kafka 消息保留在磁盘上,并在群集内复制以防止数据丢失。 Kafka 构建在 ZooKeeper 同步服务之上。 它与 Apache Storm 和 Spark 非常好地集成,用于实时流式数据分析。
什么是消息系统?
消息系统负责将数据从一个应用程序传输到另一个应用程序,因此应用程序可以专注于数据,但不担心如何共享它。 分布式消息传递基于可靠消息队列的概念。 消息在客户端应用程序和消息传递系统之间异步排队。 有两种类型的消息模式可用: 一种是点对点,另一种是发布 - 订阅 (pub-sub) 消息系统。 大多数消息模式遵循 pub-sub 。
点对点消息系统
在点对点消息系统中,消息持久化到一个队列中。此时,将有一个或多个消费者消费队列中的数据。但是一条消息只能被消费一次。当一个消费者消费了队列中的某条数据之后,该条数据则从消息队列中删除。该模式即使有多个消费者同时消费数据,也能保证数据处理的顺序。
生产者发送一条消息到 queue,只有一个消费者能收到。
发布 - 订阅消息系统
在发布-订阅消息系统中,消息被持久化到一个 topic 中。与点对点消息系统不同的是,消费者可以订阅一个或多个 topic,消费者可以消费该 topic 中所有的数据,同一条数据可以被多个消费者消费,数据被消费后不会立马删除。在发布-订阅消息系统中,消息的生产者称为发布者,消费者称为订阅者。
发布者发送到 topic 的消息,只有订阅了 topic 的订阅者才会收到消息。
Kafka优点
-
解耦
在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
-
冗余(副本)
有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的 “插入-获取-删除” 范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
-
扩展性
因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。
-
灵活性 & 峰值处理能力
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见;如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
-
可恢复性
系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
-
顺序保证
在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。Kafka 保证一个 Partition 内的消息的有序性。
-
缓冲
在任何重要的系统中,都会有需要不同的处理时间的元素。例如,加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行———写入队列的处理会尽可能的快速。该缓冲有助于控制和优化数据流经过系统的速度。
-
异步通信
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
架构
Broker
一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic;
broker 其实就是在 Hadoop 集群中的节点的概念;
broker 接收来自生产者的消息,为消息设置偏移量,并提交消息到磁盘保存;
broker 为消费者提供服务,对读取分区的请求作出响应,返回给消费者曾经生产者已经提交到磁盘上的消息。
Topic
每条发布到 Kafka 集群的消息都有一个类别,这个类别被称为 topic。物理上不同 topic 的消息分开存储,逻辑上一个 topic 的消息虽然保存于一个或多个 broker 上,但用户只需指定消息的 topic 即可生产或消费数据而不必关心数据存于何处,类似于数据库的表名。Kafka 是面向 topic 的。
Partition
为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分为多个 partition,一个 topic 至少有一个 partition,每个partition有多个副本分布在不同的 broker 中;
消息以追加的方式写入 partition,后以先先出的顺序读取;
partition 中的数据是有序的,partition 中的每条消息都会被分配一个有序的 id(offset)。如果 topic 有多个 partition,消费数据时就不能保证数据(多个 partition)的顺序。在需要严格保证消息的消费顺序的场景下,需要将 partition 数目设为1。
Leader
每个 partition 有多个副本,其中有且仅有一个作为 Leader,Leader 是当前负责数据的读写的 partition,生产者和消费者只和 Leader 互动
Follower
Follower 跟随 Leader,所有写请求都通过 Leader 路由,数据变更会广播给所有 Follower,Follower 与 Leader 保持数据同步。如果 Leader 失效,则从 Follower 中选举出一个新的 Leader。
Offset
每个 partition 在存储层面是一个 append log 文件,发布到此 partition 的消息会追加到 log 文件的尾部,为顺序写人磁盘(顺序写磁盘比随机写内存的效率还要高)。每条消息在 log 文件中的位置成为 offset(偏移量),offset 为一个 long 型数字,唯一标记一条消息。
Producer
消息生产者,该角色将消息发布到 Kafka 的 topic 中。broker 接收到生产者发送的消息后,broker 将该消息追加到当前用于追加数据的 segment 文件中。生产者发送的消息,存储到一个 partition 中,生产者也可以指定数据存储的 partition。
Consumer
消息消费者,从 kafka broker 读取消息的客户端。消费者可以消费多个 topic 中的数据。
Consumer Group
每个 consumer 都属于一个 consumer group,同一个 partition 只能被同一个 consumer group 中的一个 consumer 消费;同一个 partition 能被不同 consumer group 中的多个不同 consumer 消费。
Zookeeper
Kafka 使用 Zookeeper 保存集群的元数据信息和消费者信息,来保证集群的可用性。
Kafka 集群中只能有一个 leader,其他都是 follower, 这都需要 Zookeeper 来保证。
controller 节点回在 ZK 上注册一个临时节点,其他节点监听该临时节点。当该 controller 节点宕机后,其他 broker 争抢再次创建临时节点,保证一台新的 broker 会成为 controller 角色。
controller
一个临时节点,负责 kafka 集群的管理,主要包含:
-
感知 broker 是否宕机
-
partition leader 的选举
-
对新增 broker 的的监听
-
新增加 broker 后数据的迁移和负载均衡
-
kafka 集群的各种元数据:如 leader、partition、follower 的位置等等
-
删除 topic 后 partition 的删除
-
。。。
安装部署
官方下载地址:http://kafka.apache.org/downloads
集群规划
master | slave0 | slave1 |
---|---|---|
zk | zk | zk |
kafka | kafka | kafka |
选择最新版本进行安装。
修改解压后的文件夹。
mv kafka_2.13-2.8.0 kafka
进入kafka目录,新建 logs 文件夹。
修改 config 文件夹下的 server.properties 文件,文件内主要内容如下
# broker的全局唯一编号,不能重复. 给集群中的每个broker配置一个不同的id
broker.id=1
# 允许删除topic
delete.topic.enable=true
# 处理网络请求的线程数量
num.network.threads=3
# 用来处理磁盘IO的现成数量
num.io.threads=8
# 发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
# 接收套接字的缓冲区大小
socket.receive.buffer.bytes=102400
# 请求套接字的缓冲区大小
socket.request.max.bytes=104857600
# kafka运行日志存放的路径
log.dirs=/opt/kafka/logs
# topic在当前broker上的分区个数
num.partitions=1
# 用来恢复和清理data下数据的线程数量
num.recovery.threads.per.data.dir=1
# segment文件保留的最长时间,超时将被删除
log.retention.hours=168
# 配置连接Zookeeper集群地址
zookeeper.connect=master:2181,slave0:2181,slave1:2181
分发 kafka 文件夹到其他节点上,修改其他节点上的 broker.id 和 hostname。
简单使用
启动 Kafka 集群,默认启动是前台,日志会在客户端上跑,做不了其他操作,改为后台启动。
# 后台启动命令
bin/kafka-server-start.sh config/server.properties 1>/dev/null 2>&1 &
集群启动后使用 jps 命令查看进程,会出现一个新的名为 Kafka 的进程。
写2个脚本用于 Kafka 集群启动和关闭。
# 集群启动脚本
#!/bin/bash
# 集群节点的名称
BROKERS="master slave0 slave1"
# Kafka的安装目录
KAFKA_HOME="/opt/kafka"
for broker in $BROKERS
do
echo "INFO:starting kafka server on ${broker}"
ssh $broker "source /etc/profile;nohup ${KAFKA_HOME}/bin/kafka-server-start.sh -daemon ${KAFKA_HOME}/config/server.properties 1>/dev/null 2>&1 &"
if [ $? != 0 ];
then
echo "Can not starting kafka server on host ${broker}";
exit 1;
fi
done
# 集群关闭脚本
#!/bin/bash
for host in master slave0 slave1
do
ssh $host "source /etc/profile;jps |grep Kafka |cut -c 1-6 |xargs kill -s 9"
echo "$host kafka is stopping"
done
设置两个脚本的权限。
chmod 777 kafka-startall.sh
chmod 777 kafka-stopall.sh
集群启动和关闭效果如下。
# 集群启动
[root@master bin]# ./kafka-startall.sh
INFO:starting kafka server on master
INFO:starting kafka server on slave0
INFO:starting kafka server on slave1
# 集群关闭
[root@master bin]# ./kafka-stopall.sh
kill: cannot find process "K"
master kafka is stopping
kill: cannot find process "K"
slave0 kafka is stopping
kill: cannot find process "K"
slave1 kafka is stopping
启动集群,创建 topic。
[root@master kafka]# bin/kafka-topics.sh --create --zookeeper master:2181 --replication-factor 3 --partitions 3 --topic first
Created topic first.
几个参数如下:
- kafka-topics.sh:任何和 topic 相关的操作都使用这个命令
- –create:表示创建一个topic
- –zookeeper:指明任意一个 zookeeper 服务器地址,结果都一样
- ----replication-factor:表示每个 topic 的副本数,注意副本数必须小于等于 kafka 集群的数量
- –partitions:这个 topic 的分区的数量
- –topic:这个 topic 的名字
查看 topic 列表。
bin/kafka-topics.sh --list --zookeeper master:2181
查看某个 topic 的分区情况。
[root@master kafka]# bin/kafka-topics.sh --describe --topic first --zookeeper slave0:2181
Topic: first TopicId: fvww7fn1RLKp9MfjKrzaPA PartitionCount: 3 ReplicationFactor: 3 Configs:
Topic: first Partition: 0 Leader: 0 Replicas: 0,1,2 Isr: 0,1,2
Topic: first Partition: 1 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
Topic: first Partition: 2 Leader: 2 Replicas: 2,0,1 Isr: 2,0,1
说明一下:
- 第一行是分区的概述
- 剩下的每一行是一个分区信息,因为 topic first 有3个分区,所以有3行
- Leader: 表示这个分区的 Leader 所在的服务器 id ,这是随机分配的
- Relicas: 表示这个分区的所有复本所在的服务器 id。这只考虑 logs 中的文件, 即使 broker 挂了,也会显示
- Isr: 这个表示活着的 broker,是 Relicas 的子集
zookeeper 中的节点情况如下。
删除 topic。
bin/kafka-topics.sh --delete --topic first --zookeeper master:2181
修改 topic,topic 中一些配置参数可被修改,如修改分区数量(能改大不能改小)。
bin/kafka-topics.sh --alter --zookeeper master:2181 --topic first --partitions 4
修改后查看该 topic 的分区情况。
[root@master kafka]# bin/kafka-topics.sh --describe --topic first --zookeeper slave0:2181
Topic: first TopicId: ksBzku4pS0GrxN1mhR-TKg PartitionCount: 4 ReplicationFactor: 3 Configs:
Topic: first Partition: 0 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: first Partition: 1 Leader: 0 Replicas: 0,2,1 Isr: 0,2,1
Topic: first Partition: 2 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: first Partition: 3 Leader: 2 Replicas: 2,0,1 Isr: 2,0,1
partition 为1时,启动命令行生产者,并发送消息。
bin/kafka-console-producer.sh --broker-list master:9092 --topic first
启动命令行消费者,并接受消息。
bin/kafka-console-consumer.sh --bootstrap-server master:9092 --from-beginning --topic first
–bootstrap-server:任意一个 zookeeper 服务器地址
–from-beginning:会把 first 主题中以往所有的数据都读取出来;不添加的话只会读取以后发送的数据。
可以看到,消费者按照顺序读出了生产者生产的消息。
消费者一旦启动,会有一个新的 topic:__consumer_offsets,老版本的消费位移信息是存储的 zookeeper 中的, 但是 zookeeper 并不适合频繁的写入查询操作,所以在新版本的中消费位移信息存放在了 __consumer_offsets 内置 topic 中。
partition 为4时,启动命令行生产者,并发送消息。可以看到,消费者并没有按照消息生产的顺序接收消息。
查看消息存放位置里的文件,partition-0 保存的消息为222,partition-1 保存的消息为333、666、999,partition-2 保存的消息为111、444、555、777、888。
验证了上述说法:partition 内有序,partition 间无序。
# partition 为4时的消息发送
[root@master kafka]# bin/kafka-console-producer.sh --broker-list master:9092 --topic first
>111
>222
>333
>444
>555
>666
>777
>888
>999
# 此时消息读取情况。
[root@slave0 kafka]# bin/kafka-console-consumer.sh --bootstrap-server master:9092 --from-beginning --topic first
222
111
444
555
777
888
333
666
999
查询 topic 的 offect 范围,确定每个 partition 内的消息数量。
# offect最小值
[root@slave1 kafka]# bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list master:9092 --topic first --time -2
first:0:0
first:1:0
first:2:0
first:3:0
# offect最大值
[root@slave1 kafka]# bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list master:9092 --topic first --time -1
first:0:1
first:1:3
first:2:5
first:3:0
分区
分区分配
在创建 topic 时, Kafka 首先会决定如何在 broker 间分配 partition 。
假设你有 6 个 broker ,打算创建一个包含 10 个 partition 的 topic,并且复制系数为 3。那么 Kafka 就会有 30 (10*3) 个 partition 副本, 它们可以被分配给 6 个 broker。 在进行 partition 分配时,我们要达到如下的目标。
- 在 broker 间平均地分布 partition 副本。对于我们的例子来说,就是要保证每个 broker 可以 分到 5 个副本。
- 确保每个 partition 的每个副本分布在不同的 broker 上。假设 partition0 的 leader 副本在 broker2 上, 那么可以把 follower 副本放在 broker3 和 broker 4 上,但不能放在 broker2 上,也不能两个都放在 broker3 上。
- 如果为 broker 指定了机架信息,那么尽可能把每个 partition 的副本分配到不同机架的 broker 上。这样做是为了保证一个机架的不可用不会导致整体的分区不可用。
为了实现这个目标,我们先随机选择一个 broker(假设是 4),然后使用轮询的方式给每个 broker 分配 partition 来确定 leader partition 的位置。于是,leader partition0 会在 broker4 上,leader partition1 会在 broker5 上,leader partition2 会在 broker0 上(只有 6 个 broker),并以此类推。然后,我们从分区 leader 开始, 依次分配 follower 副本。如果 partition0 的 leader 在 broker4 上, 那么它的第一个 follower 副本会在 broker5 上, 第二个 follower 副本会在 broker0 上, …。 partition1 的 leader 在 broker5 上, 那么它的第一个 follower 副本在 broker0 上, 第二个 follower 副本在 broker1 上, …。
消息分配分区
Kafka对消息分配分区有2条核心代码,分别如下。
根据核心代码我们可知分区的规则为:
- 指定了 patition,则直接使用;
- 未指定 patition 但指定 key,通过对 key 的 value 进行 hash 出一个patition;
- patition 和 key 都未指定,使用轮询选出一个 patition。
// KafakProducer类
private int partition(ProducerRecord<K, V> record, byte[] serializedKey, byte[] serializedValue, Cluster cluster) {
Integer partition = record.partition();
return partition != null ?
partition :
partitioner.partition(
record.topic(), record.key(), serializedKey, record.value(), serializedValue, cluster);
}
// org.apache.kafka.clients.producer.internals.DefaultPartitioner类
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
// 该 topic 下所有的分区信息
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
// 该 topic 下分区数量
int numPartitions = partitions.size();
// 如果 key 字节数组为空,也就是 producer 没有指定 key 的情况下。采用轮询策略。
if (keyBytes == null) {
// 根据 topic 获取 nextValue 值
int nextValue = counter.getAndIncrement();
// 获取可用的分区信息
List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
// 如果存在可用的分区,我们就用生成的随机值取余 可用分区数 来确定该条消息发送的分区。如果不存在可用分区,就就用生成的随机值取余 分区数 来确定该条消息发送的分区。
if (availablePartitions.size() > 0) {
int part = Utils.toPositive(nextValue) % availablePartitions.size();
return availablePartitions.get(part).partition();
} else {
return Utils.toPositive(nextValue) % numPartitions;
}
} else {
//如果指定了 key 就使用一致性hash算法来指定发送的分区。
return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
}
}
消费过程
消费者和消费者群组
假设我们有一个应用程序需要从一个 Kafka 主题读取消息井验证这些消息,然后再把它们保存起来。
应用程序需要创建一个消费者对象,订阅主题并开始接收消息,然后验证消息井保存结果。
过了一阵子, 生产者往主题写入消息的速度超过了应用程序验证数据的速度,这个时候该怎么办?如果只使用单个消费者处理消息, 应用程序会远跟不上消息生成的速度。 显然, 此时很有必要对消费者进行横向伸缩。 就像多个生产者可以向相同的主题写入消息一样, 我们也可以使用多个消费者从同一个主题读取消息,对消息进行分流。
这样多个消费者从同一个主体读取消息, 就组成了消费者组。
Kafka 消费者从属于消费者群组。 一个群组里的消费者订阅的是同一个主题,每个消费者接收主题一部分分区的消息。
其实可以把这一个消费者群组看作是一个消费者,这个消费者有多个线程帮它接收消息。
往群组里增加消费者是横向伸缩消费能力的主要方式。
Kafka 消费者经常会做一些高延迟的操作,比如把数据写到数据库或 HDFS,或者使用数据进行比较耗时的计算。在这些情况下,单个消费者无法跟上数据生成的速度,所以可以增加更多的消费者,让它们分担负载,每个消费者只处理部分分区的消息,这就是横向伸缩的主要手段。
我们有必要为主题创建大量的分区,在负载增长时可以加入更多的消费者。不过要注意,不要让消费者的数量超过主题分区的数量,多余的消费者只会被闲置.
不同于传统的消息系统,横向伸缩 Kafka 消费者和消费者群组并不会对性能造成负面影响。
消费者分区再平衡
群组里的消费者 共同读取主题的分区。
一个新的悄费者加入群组时,它读取的是原本由其他消费者读取的消息。
当一个消费者被关闭或发生崩溃时,它就离开群组,原本由它读取的分区将由群组里的其他消费者来读取。
在主题发生变化时,比如管理员添加了新的分区, 会发生分区重分配。
分区的所有权从一个消费者转移到另一个消费者,这样的行为被称为再均衡。
再均衡非常重要,它为消费者群组带来了高可用性和伸缩性。
消费方式
consumer采用pull(拉)模式从 broker 中读取数据。也就是说是消费者主动从 topic 中获取数据。
push(推)模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成 consumer 来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而 pull 模式则可以根据 consumer 的消费能力以适当的速率消费消息。
对于 Kafka 而言,pull 模式更合适,它可简化 broker 的设计,consumer 可自主控制消费消息的速率,同时 consumer 可以自己控制消费方式——即可批量消费也可逐条消费,同时还能选择不同的提交方式从而实现不同的传输语义。
pull 模式不足之处是,如果 kafka 没有数据,消费者可能会陷入循环中,一直等待数据到达。为了避免这种情况,我们在我们的拉请求中有参数,允许消费者请求在等待数据到达的“长轮询”中进行阻塞(并且可选地等待到给定的字节数,以确保大的传输大小)。
消费者组配置
配置 slave0 和 slave1 机器上,kafka 文件夹下 config/consumer.properties 配置文件中的 group.id 均为 test-consumer-group。指定 consumer 配置文件后,两台机器上同时启动消费者进程,两台机器便处于同一消费组下。启动生产者生产消息,同一时刻只有一个消费者可以收到消息。
# 生产者发送消息
[root@master kafka]# bin/kafka-console-producer.sh --broker-list master:9092 --topic first
>11111
>22222
>33333
>44444
>55555
>66666
>77777
# 同一消费者组里的不同消费者消费消息
# --consumer.config config/consumer.properties指定consumer配置文件
[root@slave0 kafka]# bin/kafka-console-consumer.sh --bootstrap-server master:9092 --topic first --consumer.config config/consumer.properties
11111
33333
44444
55555
[root@slave1 kafka]# bin/kafka-console-consumer.sh --bootstrap-server master:9092 --topic first --consumer.config config/consumer.properties
22222
66666
77777
高可用
kafka 是高可用的,当 leader 挂掉时,zookeeper 将安排选举新的 leader。
原 leader broker 为 brokerid=0 的机器,在该机器上 kill Kafka 的进程,leader broker 转为 brokerid = 1 的节点(和上方对比)。
topic 中的 partition1 的 leader 已经移到 id=2 的机器上了,它的副本位于 0、1、2 这3台机器上,但处于同步状态的只id=1 和 id=2 这两台机器。
[root@slave0 kafka]# bin/kafka-topics.sh --describe --topic first --zookeeper slave0:2181
Topic: first TopicId: ksBzku4pS0GrxN1mhR-TKg PartitionCount: 4 ReplicationFactor: 3 Configs:
Topic: first Partition: 0 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: first Partition: 1 Leader: 0 Replicas: 0,2,1 Isr: 0,2,1
Topic: first Partition: 2 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: first Partition: 3 Leader: 2 Replicas: 2,0,1 Isr: 2,0,1
[root@slave0 kafka]# bin/kafka-topics.sh --describe --topic first --zookeeper slave0:2181
Topic: first TopicId: ksBzku4pS0GrxN1mhR-TKg PartitionCount: 4 ReplicationFactor: 3 Configs:
Topic: first Partition: 0 Leader: 2 Replicas: 2,1,0 Isr: 2,1
Topic: first Partition: 1 Leader: 2 Replicas: 0,2,1 Isr: 2,1
Topic: first Partition: 2 Leader: 1 Replicas: 1,0,2 Isr: 1,2
Topic: first Partition: 3 Leader: 2 Replicas: 2,0,1 Isr: 2,1
重启 id=0 的机器上的 Kafka 进程,再次查看状态,partition1 的3个副本都已经处于同步状态,但 leader 依然为 id=2 的 broker。
[root@slave0 kafka]# bin/kafka-topics.sh --describe --topic first --zookeeper slave0:2181
Topic: first TopicId: ksBzku4pS0GrxN1mhR-TKg PartitionCount: 4 ReplicationFactor: 3 Configs:
Topic: first Partition: 0 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: first Partition: 1 Leader: 2 Replicas: 0,2,1 Isr: 2,1,0
Topic: first Partition: 2 Leader: 1 Replicas: 1,0,2 Isr: 1,2,0
Topic: first Partition: 3 Leader: 2 Replicas: 2,0,1 Isr: 2,1,0
3/4 的 leader 位于 id=2 的机器上,对机器压力过大,这时候可以执行 leader 再平衡。
bin/kafka-preferred-replica-election.sh --zookeeper master:2181
再平衡后,partition1 的 leader 重新移到 id=0 的机器上。
[root@slave0 kafka]# bin/kafka-topics.sh --describe --topic first --zookeeper slave0:2181
Topic: first TopicId: ksBzku4pS0GrxN1mhR-TKg PartitionCount: 4 ReplicationFactor: 3 Configs:
Topic: first Partition: 0 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: first Partition: 1 Leader: 0 Replicas: 0,2,1 Isr: 2,1,0
Topic: first Partition: 2 Leader: 1 Replicas: 1,0,2 Isr: 1,2,0
Topic: first Partition: 3 Leader: 2 Replicas: 2,0,1 Isr: 2,1,0
扩容:将一台机器加入 kafka 集群很容易,只需要为它分配一个独立的 broker id,然后启动它即可。但是这些新加入的机器上面并没有任何的分区数据,所以除非将现有数据移动这些机器上,否则它不会做任何工作,直到创建新 topic。因此,当你往集群加入机器时,你应该将其它机器上的一部分数据往这台机器迁移。
数据迁移:数据迁移的工作需要手工初始化,然后自动完成。它的原理如下:当新机器起来后,kafka 将其它机器的一些分区复制到这个机器上,并作为 follower,当这个新机器完成复制并成为 in-sync 状态后,那些被复制的分区的一个副本会被删除。
Configs:
Topic: first Partition: 0 Leader: 2 Replicas: 2,1,0 Isr: 2,1,0
Topic: first Partition: 1 Leader: 0 Replicas: 0,2,1 Isr: 2,1,0
Topic: first Partition: 2 Leader: 1 Replicas: 1,0,2 Isr: 1,2,0
Topic: first Partition: 3 Leader: 2 Replicas: 2,0,1 Isr: 2,1,0
扩容:将一台机器加入 kafka 集群很容易,只需要为它分配一个独立的 broker id,然后启动它即可。但是这些新加入的机器上面并没有任何的分区数据,所以除非将现有数据移动这些机器上,否则它不会做任何工作,直到创建新 topic。因此,当你往集群加入机器时,你应该将其它机器上的一部分数据往这台机器迁移。
数据迁移:数据迁移的工作需要手工初始化,然后自动完成。它的原理如下:当新机器起来后,kafka 将其它机器的一些分区复制到这个机器上,并作为 follower,当这个新机器完成复制并成为 in-sync 状态后,那些被复制的分区的一个副本会被删除。