分布式消息队列Kafka,收藏这篇文章就够了
一、kafka是什么?
MQ是一个消息中间件,可以在服务器之间进行通信。
常见的消息队列模式
- 点对点Queue:一个消息只能被一个消费者接收
- 发布与订阅Topic:一个消息可以被订阅了该主题的多个消费者接收
常见的消息队列产品
- activeMQ,activeMQ可以做到事务的支持,为了数据的严谨性,业务系统一般选择activeMQ
- rabbitMQ
- zeroMQ
- rocketMQ
- kafka 只有订阅与发布
kafka是一个分布式消息队列中间件,跟传统的MQ相比,其具有高吞吐量,内部批处理的特点,主要作用是作为缓冲,来异构、结构系统。
为什么使用kafka
- 缓存作用
- 异步提供高吞吐
- 解耦
- 消息是可持久化的
kafka结构:
- 生产者:发送消息到kafka
- kafka集群:储存消息
- 消费者:订阅主题,接收消息
二、分片与副本机制
分片机制:主要解决了单台服务器存储容量有限的问题
- 当数据量非常大的时候,一个服务器存放不了,就将数据分成两个或者多个部分,存放在多台服务器上。每个服务器上的数据,叫做一个分片。
副本备份机制解决了数据存储的高可用问题
- 当数据只保存一份的时候,有丢失的风险。为了更好的容错和容灾,将数据拷贝几份,保存到不同的机器上。
三、消息不丢失机制
1、生产者端消息不丢失
1) ack消息确认分为三个状态
- 0:生产者只负责发送数据
- 1:某个partition的leader收到数据给出响应
- -1:某个partition的所有副本都收到数据后给出响应
2) 在同步模式下
- 生产者等待10S,如果broker没有给出ack响应,就认为失败。
- 生产者重试3次,如果还没有响应,就报错。
3) 在异步模式下
- 先将数据保存在生产者端的buffer中。Buffer大小是2万条。
- 满足数据阈值或者数量阈值其中的一个条件就可以发送数据。
- 发送一批数据的大小是500条。
如果broker迟迟不给ack,而buffer又满了。
开发者可以设置是否直接清空buffer中的数据。
2、broker
broker端的消息不丢失,其实就是用partition副本机制来保证。
Producer ack -1(all). 能够保证所有的副本都同步好了数据。其中一台机器挂了,并不影像数据的完整性。
3、消费者
消费者,有偏移量记录消费到哪里
只要记录offset值,消费者端不会存在消息不丢失的可能。只会重复消费。
当Kafka中的偏移量与zookeeper中的偏移量不一致就会重复消费
四、文件存储与查询机制
文件存储机制:
segment段中有两个核心的文件一个是log,一个是index。 当log文件等于1G时,新的会写入到下一个segment中。
通过下图中的数据,可以看到一个segment段差不多会存储70万条数据
查询机制如下图:
- 查找segment file
- 通过segment file查找message
五、分发策略
分发就是消息发到哪里,哪个分片
kafka数据流向
Producer —> leader partition —> follower partition(半数以上) —> consumer
- 如果是用户指定了partition,生产就不会调用DefaultPartitioner.partition()方法
数据分发策略的时候,可以指定数据发往哪个partition。
当ProducerRecord 的构造参数中有partition的时候,就可以发送到对应partition上
public ProducerRecord(String topic, Integer partition, K key, V value) {
this(topic, partition, null, key, value, null);
}
-
当用户指定key,使用hash算法,找出分片。
-
当用既没有指定partition也没有key。
使用轮询的方式发送数据。
六、负载均衡
生产者生产消息太快了,一个消费者消费不了
点对点消费
消费者组里,消费者<=分片数
七、如何保证数据消费顺序一致性
首先需要明确的是:Kafka的主题是分区有序的,如果一个主题有多个分区,那么Kafka会按照key将其发送到对应的分区中,所以,对于给定的key,与其对应的record在分区内是有序的。
Kafka可以保证同一个分区里的消息是有序的,即生产者按照一定的顺序发送消息,Broker就会按照这个顺序将他们写入对应的分区中,同理,消费者也会按照这个顺序来消费他们。
在一些场景下,消息的顺序是非常重要的。比如,先存钱再取钱与先取钱再存钱是截然不同的两种结果。
参数max.in.flight.requests.per.connections,该参数的作用是在重试次数大于等于1时,保证数据写入的顺序。如果该参数不为1,那么当第一个批次写入失败时,第二个批次写入成功,Broker会重试写入第一个批次,如果此时第一个批次重试写入成功,那么这两个批次消息的顺序就反过来了。
一般来说,如果对消息的顺序有要求,那么在为了保障数据不丢失,需要先设置发送重试次数retries>0,同时需要把max.in.flight.requests.per.connections参数设为1,这样在生产者尝试发送第一批消息时,就不会有其他的消息发送给broker,虽然会影响吞吐量,但是可以保证消息的顺序。
除此之外,还可以使用单分区的Topic,但是会严重影响吞吐量。
八、Kafka的使用
常用命令
## 启动
bin/kafka-server-start.sh config/server.properties &
## 停止
./kafka-server-stop.sh
## 查看所有的topic
./kafka-topics.sh --list --zookeeper localhost:9092
## 查看所有topic的详细信息
./kafka-topics.sh --zookeeper localhost:2181 --describe
## 列出指定topic的详细信息
./kafka-topics.sh --zookeeper localhost:2181 --describe --topic demo
## 删除一个topic
./kafka-topics.sh --zookeeper localhost:2181 --delete --topic test
## 创建一个叫test的topic,有两个分区,每个分区3个副本
./kafka-topics.sh --zookeeper localhost:2181 --create --topic test --replication-factor 3 --partitions 2
## 测试kafka发送和接收消息(启动两个终端)
#发送消息(注意端口号为配置文件里面的端口号)
./kafka-console-producer.sh --broker-list localhost:9092 --topic test
#消费消息(可能端口号与配置文件保持一致,或与发送端口保持一致)
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning #加了--from-beginning 重头消费所有的消息
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test #不加--from-beginning 从最新的一条消息开始消费
## 查看某个topic对应的消息数量
./kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list localhost:9092 --topic test --time -1
## 显示所有消费者
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
## 获取正在消费的topic(console-consumer-63307)的group的offset
./kafka-consumer-groups.sh --describe --group console-consumer-63307 --bootstrap-server localhost:9092
## 显示消费者
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
## 为topic增加副本
./kafka-reassign-partitions.sh -zookeeper127.0.0.1:2181-reassignment-json-file json/partitions-to-move.json -execute
## 为topic增加partition
./bin/kafka-topics.sh –zookeeper127.0.0.1:2181–alter –partitions20–topic testaa
如何重平衡Kafka集群
在下面情况发生时,需要重平衡集群:
- 主题分区在整个集群里的不均衡分布造成了集群负载的不均衡。
- broker离线造成分区不同步。
- 新加入的broker 需要从集群里获得负载。
使用kafka-reassign-partitions.sh命令进行重平衡
如何查看消费者组是否存在滞后消费
我们可以使用kafka-consumer-groups.sh命令进行查看,比如
$ bin/kafka-consumer-groups.sh --bootstrap-server cdh02:9092 --describe --group my-group
## 会显示下面的一些指标信息
TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST CLIENT-ID
主题 分区 当前offset LEO 滞后消息数 消费者id 主机 客户端id
一般情况下,如果运行良好,CURRENT-OFFSET的值会与LOG-END-OFFSET的值非常接近。通过这个命令可以查 看哪个分区的消费出现了滞后。
九、其他面试题
1、如何确定Kafka主题的分区数量
选择合适的分区数量可以达到高度并行读写和负载均衡的目的,在分区上达到均衡负载是实现吞吐量的关键。需要根据每个分区的生产者和消费者的期望吞吐量进行估计。
一个简单的计算公式为:分区数 = max(生产者数量,消费者数量)
- 生产者数量=整体生产吞吐量/每个生产者对单个分区的最大生产吞吐量
- 消费者数量=整体消费吞吐量/每个消费者从单个分区消费的最大吞吐量
2、如何调整生产环境中Kafka主题的分区数量
当我们增加主题的分区数量时,会违背同一个key进行同一个分区的事实。我们可以创建一个新的主题,使得该主题有更多的分区数,然后暂停生产者,将旧的主题中的数据复制到新的主题中,然后将消费者和生产者切换到新的主题,操作起来会非常棘手。