每一个分区,根据副本因子N,会有N个副本。比如在broker1上有一个topic,分区为topic-1, 副本因子为2,那么在两个broker的数据目录里,就都有一个topic-1,其中一个是leader,一个follower。
partition 物理上由多个 segment 组成,每个 Segment 存着 message 信息。
每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。
Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower。
kafka的存储文件都是按照offset.log来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.log的文件即可。当然the first offset就是00000000000.log
通常,一个典型的Kafka集群中包含若干Producer(可以是web前端产生的Page View,或者是服务器日志,系统CPU、Memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。
kafka分布式主要是指分区被分布在多台server(broker)上,同时每个分区都有leader和follower(不是必须),即老大和小弟的角色,这儿是老大负责处理,小弟负责同步,小弟可以变成老大,形成分布式模型。
kafka的分区日志(message)被分布在kafka集群的服务器上,每一个服务器处理数据和共享分区请求。每一个分区是被复制到一系列配置好的服务器上来进行容错。
每个分区有一个server节点来作为leader和零个或者多个server节点来作为followers。leader处理指定分区的所有读写请求,同时follower被动复制leader。如果leader失败,follwers中的一个将会自动地变成一个新的leader。每一个服务器都能作为分区的一个leader和作为其它分区的follower,因此kafka集群能被很好地平衡。kafka集群是一个去中心化的集群。
如上信息参考官网:http://kafka.apache.org/intro.html#intro_distribution
kafka消费的并行度就是kaka topic分区的个数,或者分区的个数决定了同一时间同一消费者组内最多可以有多少个消费者消费数据。
- 在kafka集群中,分单个broker和多个broker。每个broker中有多个topic,topic数量可以自己设定。在每个topic中又有0到多个partition,每个partition为一个分区。kafka分区命名规则为topic的名称+有序序号,这个序号从0开始依次增加。
- 每个partition中有多个segment file。创建分区时,默认会生成一个segment file,kafka默认每个segment file的大小是1G。当生产者往partition中存储数据时,内存中存不下了,就会往segment file里面刷新。在存储数据时,会先生成一个segment file,当这个segment file到1G之后,再生成第二个segment file 以此类推。每个segment file对应两个文件,分别是以.log结尾的数据文件和以.index结尾的索引文件。在服务器上,每个partition是一个目录,每个segment是分区目录下的一个文件。
- 每个segment file也有自己的命名规则,每个名字有20个字符,不够用0填充。每个名字从0开始命名,下一个segment file文件的名字就是,上一个segment file中最后一条消息的索引值。在.index文件中,存储的是key-value格式的,key代表在.log中按顺序开始第n条消息,value代表该消息的位置偏移。但是在.index中不是对每条消息都做记录,它是每隔一些消息记录一次,避免占用太多内存。即使消息不在index记录中,在已有的记录中查找,范围也大大缩小了。.index中存放的消息索引是一个稀疏索引列表。
可以想象,如果一个topic就一个分区,要是这个分区有1T数据,那么kafka就想把大文件划分到更多的目录来管理,这就是kafka所谓的分区。
- 方便在集群中扩展。因为一个topic由一个或者多个partition构成,而每个节点中通常可以存储多个partition,这样就方便分区存储与移动,也就增加其扩展性。同时也可以增加其topic的数据量。
- 可以提高并发。因为一个主题多个partition,而每个主题读写数据时,其实就是读写不同的partition,所以增加其并发。
Kafka集群只有一个broker,默认/var/log/kafka-log为数据文件存储根目录,在Kafka broker中server.properties文件配置(参数log.dirs=/opt/data/kafka),例如创建2个topic名称分别为test-1、test-2, partitions数量都为partitions=4
存储路径和目录规则为:
|--test-1-0
|--test-1-1
|--test-1-2
|--test-1-3
|--test-2-0
|--test-2-1
|--test-2-2
|--test-2-3
在Kafka文件存储中,同一个topic下有多个不同partition,每个partition为一个目录,partiton命名规则为:topic名称+分区编号(有序),第一个partiton序号从0开始,序号最大值为partitions数量减1。
- 将所有broker(n个)和partition排序
- 将第i个Partition分配到第(i mode n)个broker上
test3的topic,4个分区,2个副本。
[root@qianfeng01 kafka]# kafka-topics.sh --describe --zookeeper qianfeng01:2181,qianfeng02:2181,qianfeng03:2181/kafka --topic test3
Topic:test3 PartitionCount:4 ReplicationFactor:2 Configs:
Topic: test3 Partition: 0 Leader: 1 Replicas: 1,3 Isr: 1,3
Topic: test3 Partition: 1 Leader: 2 Replicas: 2,1 Isr: 1,2
Topic: test3 Partition: 2 Leader: 3 Replicas: 3,2 Isr: 2,3
Topic: test3 Partition: 3 Leader: 1 Replicas: 1,2 Isr: 1,2
第1个Partition分配到第(1 mode 3)= 1个broker上
第2个Partition分配到第(2 mode 3)= 2个broker上
第3个Partition分配到第(3 mode 3)= 3个broker上
第4个Partition分配到第(4 mode 3)= 1个broker上
- 在Kafka集群中,每个Broker都有均等分配Partition的Leader机会。
- 上述图Broker Partition中,箭头指向为副本,以Partition-0为例:broker3中parition-0为Leader,Broker1中Partition-0为副本。
- 上述图种每个Broker(按照BrokerId有序)依次分配主Partition,下一个Broker为副本,如此循环迭代分配,多副本都遵循此规则。
- 将所有N Broker和待分配的i个Partition排序。
- 将第i个Partition分配到第(i mod n)个Broker上。
- 将第i个Partition的第j个副本分配到第((i + j) mod n)个Broker上。
[root@qianfeng01 kafka]# kafka-topics.sh --describe --zookeeper qianfeng01:2181,qianfeng02:2181,qianfeng03:2181/kafka --topic test1 Topic:test1 PartitionCount:3 ReplicationFactor:2 Configs:
Topic: test1 Partition: 0 Leader: 3 Replicas: 3,1 Isr: 3,1
Topic: test1 Partition: 1 Leader: 1 Replicas: 1,2 Isr: 1,2
Topic: test1 Partition: 2 Leader: 2 Replicas: 2,3 Isr: 2,3
第0个paritition分配到第(0%3)个broker上,即分配到第1个broker上。0分区的第1个副本在((0+1)%3)=1个broker
第1个paritition分配到第(1%3)个broker上,即分配到第2个broker上。
第2个paritition分配到第(2%3)个broker上,即分配到第3个broker上。
- 如果指定了partition,进入该partition
- 如果没有指定该partition,但是指定key,通过key的字节数组信息的hashcode值和partition数求模确定partition
- 如果都没有指定,通过轮询方式进入对应的partition。
- 每个分区一个目录,该目录中是一堆segment file(默认一个segment是1G),该目录和file都是物理存储于磁盘。
- 每个partion(目录)相当于一个巨型文件被平均分配到多个大小相等segment(段)数据文件中。但每个段segment file消息数量不一定相等,这种特性方便old segment file快速被删除。
- 每个partiton只需要支持顺序读写就行了,segment文件生命周期由服务端配置参数决定。
- 这样做的好处就是能快速删除无用文件,有效提高磁盘利用率。
查找offset=23066的message,需要通过如下2个步骤查找:
00000000000000000000.index
00000000000000000000.log
00000000000000023060.index
00000000000000023060.log
根据.index和.log物理结构对应关系图可知,其中00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0.第二个文件00000000000000023060.index的消息量起始偏移量为23060 = 23059 + 1.同样,其他后续文件依次类推,以起始偏移量命名并排序这些文件,只要根据offset 二分查找文件列表,就可以快速定位到具体文件。
当offset=23066时定位到0000000000000023060.index和log文件。
通过第一步定位到segment file,当offset=23066时,依次定位到0000000000000023060.index的元数据物理位置和 0000000000000023060.log的物理偏移地址,然后再通过0000000000000023060.log顺序查找直到 offset=23066为止。
segment index file采取稀疏索引存储方式,即<偏移量、位置>,它减少索引文件大小,通过map可以直接内存操作,稀疏索引为数据文件的每个对应message设置一个元数据指针,它比稠密索引节省了更多的存储空间,但查找起来需要消耗更多的时间。
consumer group是kafka提供的可扩展且具有容错性的消费者机制。既然是一个组,那么组内必然可以有多个消费者或消费者实例(consumer instance),它们共享一个公共的ID,即group ID。组内的所有消费者协调在一起来消费订阅主题(subscribed topics)的所有分区(partition)。当然,每个分区只能由同一个消费组内的一个consumer来消费。理解consumer group记住下面这三个特性就好了:
- consumer group下可以有一个或多个consumer instance,consumer instance可以是一个进程,也可以是一个线程
- group.id是一个字符串,唯一标识一个consumer group
- consumer group下订阅的topic下的每个分区只能分配给某个group下的一个consumer(当然该分区还可以被分配给其他group)
由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置的继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费。
Kafka默认是定期帮你自动提交位移的(enable.auto.commit = true),你当然可以选择手动提交位移实现自己控制。另外kafka会定期把group消费情况保存起来,做成一个offset map,如下图所示:
Kafka 0.9版本之前,consumer默认将offset保存在Zookeeper中,zk中的目录结构是:/consumers/
/offsets//。但是zookeeper其实并不适合进行大批量的读写操作,尤其是写操作。 因此从0.9版本开始,consumer默认将offset保存在Kafka一个内置的topic中,该topic为__consumer_offsets。该topic的格式大概如下: group.id:分组id,唯一。
- 将zookeeper维护offset 的方式称为 low level API
- 将kafka broker 维护offset的方式称为high level API
使用high level API 更新offset具体设置
consumer中设置可以在代码中设这个属性
- 自动提交,设置enable.auto.commit=true,更新的频率根据参数【auto.commit.interval.ms】来定。这种方式也被称为【at most once】,fetch到消息后就可以更新offset,无论是否消费成功。默认就是true
- 手动提交,设置enable.auto.commit=false,这种方式称为【at least once】。fetch到消息后,等消费完成再调用方法【consumer.commitSync()】,手动更新offset;如果消费失败,则offset也不会更新,此条消息会被重复消费一次。
一个较早问题是我们应该考虑是消费者从broker中pull数据还是broker将数据push给消费者。kakfa遵守传统设计和借鉴很多消息系统,这儿kafka选择producer向broker去push消息,并由consumer从broker pull消息。一些ogging-centric system,比如Facebook的Scribe和Cloudera的Flume,采用非常不同的push模式。事实上,push模式和pull模式各有优劣。
push模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。push模式的目标是尽可能以最快速度传递消息,但是这样很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而pull模式则可以根据consumer的消费能力以适当的速率消费消息。
pull模式不足之处是,如果kafka没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,Kafka的消费者在消费数据时会传入一个时长参数timeout,如果当前没有数据可供消费,consumer会等待一段时间之后再返回,这段时长即为timeout。
ps:timeout官方案例是100毫秒
为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后,都需要向producer发送ack(acknowledgement确认收到),如果producer收到ack,就会进行下一轮的发送,否则重新发送数据。
副本数据同步策略
方案 | 优点 | 缺点 |
---|---|---|
半数以上完成同步,就发送ack | 延迟低 | 选举新的leader时,容忍n台节点的故障,需要2n+1个副本 |
全部完成同步,才发送ack | 选举新的leader时,容忍n台节点的故障,需要n+1个副本 | 延迟高 |
Kafka选择了第二种方案,原因如下:
1.同样为了容忍n台节点的故障,第一种方案需要2n+1个副本,而第二种方案只需要n+1个副本,而Kafka的每个分区都有大量的数据,第一种方案会造成大量数据的冗余。
2.虽然第二种方案的网络延迟会比较高,但网络延迟对Kafka的影响较小。
2)ISR
采用第二种方案之后,设想以下情景:leader收到数据,所有follower都开始同步数据,但有一个follower,因为某种故障,迟迟不能与leader进行同步,那leader就要一直等下去,直到它完成同步,才能发送ack。这个问题怎么解决呢?
Leader维护了一个动态的in-sync replica set (ISR),意为和leader保持同步的follower集合。当ISR中的follower完成数据的同步之后,leader就会给follower发送ack。如果follower长时间未向leader同步数据,则该follower将被踢出ISR,该时间阈值由replica.lag.time.max.ms参数设定。Leader发生故障之后,就会从ISR中选举新的leader。
注:
- 生产者发送到特定主题分区的消息是将按照发送的顺序来追加。也就是说,如果消息M1和消息M2由相同的生产者发送,并且M1是先发送的,那么M1的偏移量将比M2低,并出现在日志的前面。
- 消费者是按照存储在日志中记录顺序来查询消息。
- 对于具有n个副本的主题,我们将容忍最多N-1个服务器失败故障,从而不会丢失提交到日志的任何消息记录。
对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没必要等ISR中的follower全部接收成功。
所以Kafka为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择以下的配置。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XfxIGFyy-1666261894431)(pic/image-20220225085713457.png)]
Ack级别:
(1)follower故障
follower发生故障后会被临时踢出ISR,待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步。等该follower的LEO大于等于该Partition的HW,即follower追上leader之后,就可以重新加入ISR了。
(2)leader故障
leader发生故障之后,会从ISR中选出一个新的leader,之后,为保证多个副本之间的数据一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。
注意:这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。
造成两个问题(数据丢失和数据重复)的根本原因在于
- HW值被用于衡量副本备份的成功与否。
- 在出现失败重启时作为日志截断的依据。
Kafka从0.11引入了 leader epoch 来取代HW值。Leader端使用内存保存Leader的epoch信息,即使出现上面的两个场景也能规避这些问题。
所谓Leader epoch实际上是一对值:
:
epoch表示Leader的版本号,从0开始,Leader变更过1次,epoch+1
offset对应于该epoch版本的Leader写入第一条消息的offset。因此假设有两对值:
<0, 0> <1, 120>
则表示第一个Leader从位移0开始写入消息;共写了120条[0, 119];而第二个Leader版本号是1,从位移120处开始写入消息。
- Leader broker中会保存这样的一个缓存,并定期地写入到一个 checkpoint 文件中。
- 当Leader写Log时它会尝试更新整个缓存:如果这个Leader首次写消息,则会在缓存中增加一个条目;否则就不做更新。
- 每次副本变为Leader时会查询这部分缓存,获取出对应Leader版本的位移,则不会发生数据不一致和丢失的情况。
只需要知道每个副本都引入了新的状态来保存自己当leader时开始写入的第一条消息的offset以及leader版本。这样在恢复的时候完全使用这些信息而非HW来判断是否需要截断日志。
依靠Leader epoch的信息可以有效地规避数据不一致的问题。
注意**:**对于使用 unclean.leader.election.enable = true 设置的群集,该方案不能保证消息的一致性。
对于某些比较重要的消息,我们需要保证exactly once语义,即保证每条消息被发送且仅被发送一次。
在0.11版本之后,Kafka引入了幂等性机制(idempotent),配合acks = -1时的at least once(最少一次)语义,实现了producer到broker的exactly once语义。
*idempotent + at least once = exactly once*
使用时,只需将enable.idempotence属性设置为true(在生产者的位置),kafka自动将acks属性设为-1。
ps:幂等性机制是什么意思,幂等简单说1的几次幂都等于1,也就是说一条消息无论发几次都只算一次,无论多少条消息但只实例化一次
kafka完成幂等性其实就是给消息添加了唯一ID, 这个ID的组成是PID(ProducerID)这样保证每一个Producer发送的时候是唯一的,还会为Producer中每条消息添加一个消息ID,也就是说当前Producer中生产的消息会加入Producer的ID和消息ID这样就能保证消息唯一了,这个消息发送到Kafka中的时候回暂时缓存ID,写入数据后没有收到ack,那么会从新发送这个消息,新消息过来的时候会和缓存中ID进行比较如果发现已经存在就不会再次接受了
为了实现Producer的幂等性,Kafka引入了Producer ID(即PID)和Sequence Number。
PID。每个新的Producer在初始化的时候会被分配一个唯一的PID,这个PID对用户是不可见的。
Sequence Numbler。(对于每个PID,该Producer发送数据的每个
都对应一个从0开始单调递增的Sequence Number Kafka可能存在多个生产者,会同时产生消息,但对Kafka来说,只需要保证每个生产者内部的消息幂等就可以了,所有引入了PID来标识不同的生产者。
对于Kafka来说,要解决的是生产者发送消息的幂等问题。也即需要区分每条消息是否重复。
Kafka通过为每条消息增加一个Sequence Numbler,通过Sequence Numbler来区分每条消息。每条消息对应一个分区,不同的分区产生的消息不可能重复。所有Sequence Numbler对应每个分区 Broker端在缓存中保存了这seq number,对于接收的每条消息,如果其序号比Broker缓存中序号大于1则接受它,否则将其丢弃。这样就可以实现了消息重复提交了。但是,只能保证单个Producer对于同一个
的Exactly Once语义。不能保证同一个Producer一个topic不同的partition幂等。
package com.qf.bigdata.kafka;
import kafka.common.KafkaException;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Properties;
/**
* 开启事务机制,底层默认启动幂等性
*/
public class _11ProducerTransactional {
public static void main(String[] args) {
// 加载配置文件
Properties props = new Properties();
props.put("bootstrap.servers", "qianfeng01:9092,qianfeng02:9092,qianfeng03:9092");
// 开启事务机制,设置事务ID
props.put("transactional.id","Test"+System.currentTimeMillis());
// 设置副本个数,一定大于3个副本
props.put("replication.factor","3");
// 写入副本的时候,写入多少副本算成功写入
props.put("min.insync.replicas","2");
// 设置序列化方式
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
/**
* 发送数据到Kafka集群
*/
Producer<String, String> producer = new KafkaProducer<>(props);
// 事务初始化操作
producer.initTransactions();
try{
// 开启事务功能
producer.beginTransaction();
// 发送kafka数据,有三个参数,时间戳(默认自带)、Key(可以不写,默认为空),Value(必须写,这个是数据)
ProducerRecord<String, String> record = null;
// 循环发送
for (int i = 0; i < 10; i++){
// 指定Key以后就是一种Hash方式
record = new ProducerRecord<>("spark","abcabcabcabcabc"+i);
// 发送数据
producer.send(record);
if(i==5){
throw new KafkaException("失败");
}
// 不添加线程等待,数据正常处理,不会发送数据过去,
// 当添加了线程等待,此时数据发送有问题,那么消费者将接收到数据
// 此时需要开启消费者隔离性
// Thread.sleep(1000);
}
// 提交事务
producer.commitTransaction();
}catch (Exception e){
// 如果遇到发送任务失败的情况,需要回滚数据
producer.abortTransaction();
e.printStackTrace();
}finally {
// 发送完成后关闭连接
producer.close();
}
}
}
未开启消费者隔离性测试
[root@qianfeng01 kafka_2.12-2.4.1]# ./bin/kafka-console-consumer.sh --bootstrap-server qianfeng01:9092 --topic spark
开启消费者隔离测试
[root@qianfeng01 kafka_2.12-2.4.1]# ./bin/kafka-console-consumer.sh --bootstrap-server qianfeng01:9092 --topic spark --isolation-level read_committed
Kafka集群中有一个broker会被选举为Controller,负责管理集群broker的上下线,所有topic的分区副本分配和leader选举等工作。
Controller的管理工作都是依赖于Zookeeper的。
只有KafkaController Leader会向zookeeper上注册Watcher,其他broker几乎不用监听zookeeper的状态变化。
Kafka集群中多个broker,有一个会被选举为controller leader(谁先到就是谁),负责管理整个集群中分区和副本的状态,比如partition的leader 副本故障,由controller 负责为该partition重新选举新的leader 副本;当检测到ISR列表发生变化,有controller通知集群中所有broker更新其MetadataCache信息;或者增加某个topic分区的时候也会由controller管理分区的重新分配工作
当broker启动的时候,都会创建KafkaController对象,但是集群中只能有一个leader对外提供服务,这些每个节点上的KafkaController会在指定的zookeeper路径下创建临时节点,只有第一个成功创建的节点的KafkaController才可以成为leader,其余的都是follower。当leader故障后,所有的follower会收到通知,再次竞争在该路径下创建节点从而选举新的leader
日志允许序列附加,总是附加到最后一个文件。当该文件达到可配置的大小(比如1GB)时,就会将其刷新到一个新文件。日志采用两个配置参数:M和S,前者给出在强制OS将文件刷新到磁盘之前要写入的消息数量(条数),后者给出多少秒之后被强制刷新。这提供了一个持久性保证,在系统崩溃的情况下最多丢失M条消息或S秒的数据。
1、读取的实际过程是:首先根据offset去定位数据文件中的log segment文件,然后从全局的offset值中计算指定文件offset,然后从指定文件offset读取消息。查找使用的是二分查找(基于快排队segment文件名进行排序),每一个文件的范围都被维护到内存中。
2、读取是通过提供消息的64位逻辑偏移量(8字节的offset)和s字节的最大块大小来完成。
3、读取将返回一个迭代器包含有s字节的缓冲区,缓冲区中含有消息。S字节应该比任何单个消息都大,但是在出现异常大的消息时,可以多次重试读取,每次都将缓冲区大小加倍,直到成功读取消息为止。
4、可以指定最大的消息和缓冲区大小,以使服务器拒绝的消息大于某个大小,并为客户机提供其获得完整消息所需的最大读取量。
1、读取的实际过程是:首先根据offset去定位数据文件中的log segment文件,然后从全局的offset值中计算指定文件offset,然后从指定文件offset读取消息。查找使用的是二分查找,每一个文件的范围都被维护到内存中。
2、读取是通过提供消息的64位逻辑偏移量(8字节的offset)和s字节的最大块大小来完成。
3、读取将返回一个迭代器包含有s字节的缓冲区,缓冲区中含有消息。S字节应该比任何单个消息都大,但是在出现异常大的消息时,可以多次重试读取,每次都将缓冲区大小加倍,直到成功读取消息为止。
4、可以指定最大的消息和缓冲区大小,以使服务器拒绝的消息大于某个大小,并为客户机提供其获得完整消息所需的最大读取量。
1、日志提供了一个配置参数M,该参数控制在强制刷新磁盘之前写入的消息的最大数量(M条)。
2、启动日志恢复去处理最近消息在总消息中是否有效,使用crc32来校验,如果消息长度和offset总和小于文件长度且crc32和存储的消息能匹配上,则表示有效。
请注意:
必须处理两种类型的损坏:中断(由于崩溃而丢失未写的块)和损坏(向文件添加无意义块)。
为了简化开发者和服务工程师维护Kafka集群的工作有一个监控管理工具,叫做 Kafka-eagle。这个管理工具可以很容易地发现分布在集群中的哪些topic分布不均匀,或者是分区在整个集群分布不均匀的的情况。它支持管理多个集群、选择副本、副本重新分配以及创建Topic。同时,这个管理工具也是一个非常好的可以快速浏览这个集群的工具,
- 官网地址:http://www.kafka-eagle.org/
- 开源地址:https://github.com/smartloli/kafka-eagle
Kafka Eagle所包含的功能有数据面板、数据大屏、主题列表、消费者组、集群详情、监控中心、告警策略、系统管理等功能
解压
这里我们选择将kafak-eagle安装在第一台
直接将kafka-eagle安装包上传到node1服务器的任意路径下,然后进行解压
node1服务器执行一下命令进行解压
[root@qianfeng01 ~]# tar -zxvf kafka-eagle-bin-2.1.0.tar.gz -C /usr/local/
[root@qianfeng01 ~]# cd /usr/local/kafka-eagle-bin-2.1.0/
[root@qianfeng01 kafka-eagle-bin-2.1.0]# tar -zxvf efak-web-2.1.0-bin.tar.gz
修改配置文件
[root@qianfeng01 efak-web-2.1.0]# vim conf/system-config.properties
######################################
# multi zookeeper & kafka cluster list
# Settings prefixed with 'kafka.eagle.' will be deprecated, use 'efak.' instead
######################################
efak.zk.cluster.alias=cluster1
cluster1.zk.list=qianfeng01:2181,qianfeng02:2181,qianfeng03:2181/kafka
######################################
# zookeeper enable acl
######################################
cluster1.zk.acl.enable=false
cluster1.zk.acl.schema=digest
cluster1.zk.acl.username=test
cluster1.zk.acl.password=test123
######################################
# broker size online list
######################################
cluster1.efak.broker.size=20
######################################
# zk client thread limit
######################################
kafka.zk.limit.size=16
######################################
# EFAK webui port
######################################
efak.webui.port=8048
######################################
# kafka jmx acl and ssl authenticate
######################################
cluster1.efak.jmx.acl=false
cluster1.efak.jmx.user=keadmin
cluster1.efak.jmx.password=keadmin123
cluster1.efak.jmx.ssl=false
cluster1.efak.jmx.truststore.location=/data/ssl/certificates/kafka.truststore
cluster1.efak.jmx.truststore.password=ke123456
######################################
# kafka offset storage
######################################
cluster1.efak.offset.storage=kafka
cluster2.efak.offset.storage=zk
######################################
# kafka jmx uri
######################################
cluster1.efak.jmx.uri=service:jmx:rmi:///jndi/rmi://%s/jmxrmi
######################################
# kafka metrics, 15 days by default
######################################
efak.metrics.charts=true
efak.metrics.retain=15
######################################
# kafka sql topic records max
######################################
efak.sql.topic.records.max=5000
efak.sql.topic.preview.records.max=10
######################################
# delete kafka topic token
######################################
efak.topic.token=keadmin
######################################
# kafka sasl authenticate
######################################
cluster1.efak.sasl.enable=false
cluster1.efak.sasl.protocol=SASL_PLAINTEXT
cluster1.efak.sasl.mechanism=SCRAM-SHA-256
cluster1.efak.sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="kafka" password="kafka-eagle";
cluster1.efak.sasl.client.id=
cluster1.efak.blacklist.topics=
cluster1.efak.sasl.cgroup.enable=false
cluster1.efak.sasl.cgroup.topics=
cluster2.efak.sasl.enable=false
cluster2.efak.sasl.protocol=SASL_PLAINTEXT
cluster2.efak.sasl.mechanism=PLAIN
cluster2.efak.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="kafka" password="kafka-eagle";
cluster2.efak.sasl.client.id=
cluster2.efak.blacklist.topics=
cluster2.efak.sasl.cgroup.enable=false
cluster2.efak.sasl.cgroup.topics=
######################################
# kafka ssl authenticate
######################################
cluster3.efak.ssl.enable=false
cluster3.efak.ssl.protocol=SSL
cluster3.efak.ssl.truststore.location=
cluster3.efak.ssl.truststore.password=
cluster3.efak.ssl.keystore.location=
cluster3.efak.ssl.keystore.password=
cluster3.efak.ssl.key.password=
cluster3.efak.ssl.endpoint.identification.algorithm=https
cluster3.efak.blacklist.topics=
cluster3.efak.ssl.cgroup.enable=false
cluster3.efak.ssl.cgroup.topics=
######################################
# kafka sqlite jdbc driver address
######################################
#efak.driver=org.sqlite.JDBC
#efak.url=jdbc:sqlite:/hadoop/kafka-eagle/db/ke.db
#efak.username=root
#efak.password=www.kafka-eagle.org
######################################
# kafka mysql jdbc driver address
######################################
efak.driver=com.mysql.cj.jdbc.Driver
efak.url=jdbc:mysql://qianfeng01:3306/ke?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
efak.username=root
efak.password=123456
配置环境变量
[root@qianfeng01 efak-web-2.1.0]# vim /etc/profile
export KE_HOME=/usr/local/kafka-eagle-bin-2.1.0/efak-web-2.1.0
$PATH:${KE_HOME}/bin
[root@qianfeng01 efak-web-2.1.0]# source /etc/profile
修改命令
# 分别修改每台kafka的启动命令kafka-server-start.sh
vi
if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
export KAFKA_HEAP_OPTS="-server -Xms2G -Xmx2G -XX:PermSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=8 -XX:ConcGCThreads=5 -XX:InitiatingHeapOccupancyPercent=70"
# 这里的端口不一定非要设置成9999,端口只要可用,均可。
export JMX_PORT="9999"
#export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
fi
手动创建Ke数据库
命令
# 启动Kafka Eagle系统,执行如下命令:
ke.sh start
命令 | 说明 |
---|---|
ke.sh start | 启动Kafka Eagle系统 |
ke.sh stop | 停止Kafka Eagle系统 |
ke.sh restart | 重启Kafka Eagle系统 |
ke.sh status | 查看Kafka Eagle系统运行状态 |
ke.sh stats | 统计Kafka Eagle系统占用Linux资源情况 |
ke.sh find [ClassName] | 查看Kafka Eagle系统中的类是否存在 |
访问:http://host:8048/ke