kafka学习笔记
1.介绍
Kafka 是⼀个分布式的、⽀持分区的(partition)、多副本的 (replica),基于 zookeeper 协调的分布式消息系统,它最大的特性就是可以实时处理大量数据以满足各类需求场景:
- 日志收集:使用 Kafka 收集各种服务的日志,并通过 kafka 以统一接口服务的方式开放给各种 consumer,例如 hadoop、Hbase、Solr 等
- 消息系统:解耦和生产者和消费者、缓存消息等
- 用户活动跟踪:Kafka 经常被用来记录 web 用户或者 app 用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到 kafka 的 topic 中,然后订阅者通过订阅这些 topic 来做实时的监控分析,或者装载到 hadoop、数据仓库中做离线分析和挖掘
- 运营指标:Kafka 也经常用来记录运营监控数据,包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告
2.相关术语
名称 | 解释 |
---|---|
Broker | 消息中间件处理节点,⼀个 Kafka 节点就是⼀个 broker,⼀个或者多个 Broker 可以组成⼀个 Kafka 集群 |
Topic | Kafka 根据 topic 对消息进行归类,发布到 Kafka 集群的每条消息都需要指定⼀个 topic |
Producer | 消息生产者,向 Broker 发送消息的客户端 |
Consumer | 消息消费者,从 Broker 读取消息的客户端 |
ConsumerGroup | 每个 Consumer 属于⼀个特定的 Consumer Group,⼀条消息可以被多个不同的 Consumer Group 消费,但是⼀个 Consumer Group 中只能有⼀个 Consumer 能够消费该消息 |
Partition | 物理上的概念,⼀个 topic 可以分为多个 partition,每个 partition 内部消息是有序的 |
3.安装
安装 Kafka 之前需要先安装 JDK 和 Zookeeper,在官网下载 Kafka 安装包:http://kafka.apache.org/downloads,直接解压即可
需要修改配置文件,进⼊到 config 目录内,修改 server.properties
server.properties 核心配置详解
Property | Default | Description |
---|---|---|
broker.id | 0 | 每个 broker 都可以用⼀个唯⼀的非负整数 id 进行标识,作为 broker 的 名字 |
log.dirs | /tmp/kafka-logs | kafka 存放数据的路径,这个路径并不是唯⼀的,可以是多个,路径之间只需要使⽤逗号分隔即可;每当创建新 partition 时,都会选择在包含最少 partitions 的路径下进行 |
listeners | PLAINTEXT://192.168.65.60:9092 | server 接受客户端连接的端⼝,ip 配置 kafka 本机 ip 即可 |
zookeeper.connect | localhost:2181 | zooKeeper 连接字符串的格式为:hostname:port,此处 hostname 和 port 分别是 ZooKeeper 集群中某个节点的 host 和 port;zookeeper 如果是集群,连接⽅式为 hostname1:port1,hostname2:port2,hostname3:port3 |
log.retention.hours | 168 | 每个日志文件删除之前保存的时间,默认数据保存时间对所有 topic 都⼀样 |
num.partitions | 1 | 创建 topic 的默认分区数 |
default.replication.factor | 1 | ⾃动创建 topic 的默认副本数量,建议设置为⼤于等于 2 |
min.insync.replicas | 1 | 当 producer 设置 acks 为 -1 时,min.insync.replicas 指定 replicas 的最小数目(必须确认每⼀个 repica 的写数据都是成功的),如果这个数目没有达到,producer 发送消息会产生异常 |
delete.topic.enable | false | 是否允许删除主题 |
进入到 bin 目录下,使用命令来启动
./kafka-server-start.sh -daemon../config/server.properties
验证是否启动成功:进入到 zk 中的节点看 id 是 0 的 broker 有没有存在(上线)
ls /brokers/ids/
主题 Topic
topic 可以实现消息的分类,不同消费者订阅不同的 topic
执行以下命令创建名为 test
的 topic,这个 topic 只有一个 partition,并且备份因子也设置为 1
./kafka-topics.sh --create --zookeeper 172.16.253.35:2181 --replication-factor 1 --partitions 1 --topic test
查看当前 kafka 内有哪些 topic
./kafka-topics.sh --list --zookeeper 172.16.253.35:2181
发送消息
把消息发送给 broker 中的某个 topic,打开⼀个 kafka 发送消息的客户端,然后开始⽤客户端向 kafka 服务器发送消息
kafka 自带了一个 producer 命令客户端,可以从本地文件中读取内容,或者我们也可以以命令行中直接输入内容,并将这些内容以消息的形式发送到 kafka 集群中。在默认情况下,每一个行会被当做成一个独立的消息
./kafka-console-producer.sh --broker-list 172.16.253.38:9092 --topic test
消费消息
对于 consumer,kafka 同样也携带了一个命令行客户端,会将获取到内容在命令中进行输出,默认是消费最新的消息。使用 kafka 的消费者客户端,从指定 kafka 服务器的指定 topic 中消费消息
方式一:从最后一条消息的 偏移量+1 开始消费
./kafka-console-consumer.sh --bootstrap-server 172.16.253.38:9092 --topic test
方式二:从头开始消费
./kafka-console-consumer.sh --bootstrap-server 172.16.253.38:9092 --from-beginning --topic test
消息的发送方会把消息发送到 broker 中,broker 会存储消息,消息是按照发送的顺序进行存储。因此消费者在消费消息时可以指明主题中消息的偏移量。默认情况下,是从最后一个消息的下一个偏移量开始消费
单播消息
一个消费组里只有一个消费者能消费到某一个 topic 中的消息,可以创建多个消费者,这些消费者在同一个消费组中
./kafka-console-consumer.sh --bootstrap-server 10.31.167.10:9092 --consumer-property group.id=testGroup --topic test
多播消息
在一些业务场景中需要让一条消息被多个消费者消费,那么就可以使用多播模式。kafka 实现多播,只需要让不同的消费者处于不同的消费组即可
./kafka-console-consumer.sh --bootstrap-server 10.31.167.10:9092 --consumer-property group.id=testGroup1 --topic test
./kafka-console-consumer.sh --bootstrap-server 10.31.167.10:9092 --consumer-property group.id=testGroup2 --topic test
查看消息组及信息
./kafka-consumer-groups.sh --bootstrap-server 10.31.167.10:9092 --list
./kafka-consumer-groups.sh --bootstrap-server 172.16.253.38:9092 --describe --group testGroup
- Currennt-offset:当前消费组的已消费偏移量
- Log-end-offset:主题对应分区消息的结束偏移量(HW)
- Lag:当前消费组未消费的消息数
主题与分区
主题 Topic 在 kafka 中是⼀个逻辑概念,kafka 通过 topic 将消息进行分类。不同的 topic 会被订阅该 topic 的消费者消费。但是有⼀个问题,如果说这个 topic 的消息非常多,消息是会被保存到 log 日志文件中的,这会出现文件过大的问题,因此,kafka 提出了 Partition 分区的概念
通过 partition 将⼀个 topic 中的消息分区来存储,这样的好处有多个:
- 分区存储,可以解决存储文件过大的问题
- 提供了读写的吞吐量:读和写可以同时在多个分区进⾏
为⼀个主题创建多个分区
./kafka-topics.sh --create --zookeeper localhost:2181 --partitions 2 --topic test1
通以下命令查看 topic 的分区信息
./kafka-topics.sh --describe --zookeeper localhost:2181 --topic test1
分区的作用:
- 可以分布式存储
- 可以并行写
了解了 Partition,再补充一个 Kafka 细节:在消息日志文件中,kafka 内部创建了 __consumer_offsets 主题包含了 50 个分区。这个主题用来存放消费者某个主题的偏移量,每个消费者会把消费的主题的偏移量自主上报给 kafka 中的默认主题:consumer_offsets。因此 kafka 为了提升这个主题的并发性,默认设置了 50 个分区
- 提交到哪个分区:通过 hash 函数:
hash(consumerGroupId) % __consumer_offsets 主题的分区数
- 提交到该主题中的内容是:key 是 consumerGroupId + topic + 分区号,value 就是当前 offset 的值
- 文件中保存的消息,默认保存七天,七天到后消息会被删除
kafka集群
创建三个 server.properties 文件
# 0 1 2
broker.id=2
# 9092 9093 9094
listeners=PLAINTEXT://192.168.65.60:9094
kafka-logs kafka-logs-1 kafka-logs-2
log.dir=/usr/local/data/kafka-logs-2
通过命令启动三台 broker
./kafka-server-start.sh -daemon../config/server0.properties
./kafka-server-start.sh -daemon../config/server1.properties
./kafka-server-start.sh -daemon../config/server2.properties
搭建完后通过查看 zk 中的 /brokers/ids 看是否启动成功
副本
下面的命令,在创建主题时,除了指明了主题的分区数以外,还指明了副本数,分别是:一个主题,两个分区、三个副本
./kafka-topics.sh --create --zookeeper 172.16.253.35:2181 --replication-factor 3 --partitions 2 --topic my-replicated-topic
通过查看主题信息,其中的关键数据:
- replicas:当前副本所存在的 broker 节点
- leader:副本里的概念
- 每个 partition 都有一个 broker 作为 leader
- 消息发送方要把消息发给哪个 broker,就看副本的 leader 是在哪个 broker 上面,副本里的 leader 专门用来接收消息
- 接收到消息,其他 follower 通过 poll 的方式来同步数据
- follower:leader 处理所有针对这个 partition 的读写请求,而 follower 被动复制 leader,不提供读写(主要是为了保证多副本数据与消费的一致性),如果 leader 所在的 broker 挂掉,那么就会进行新 leader 的选举
- isr:可以同步的 broker 节点和已同步的 broker 节点,存放在 isr 集合中
broker 主题、分区、副本
Kafka 集群中由多个 broker 组成,⼀个 broker 存放⼀个 topic 的不同 partition 以及它们的副本
集群消息的发送
./kafka-console-producer.sh --broker-list 172.16.253.38:9092,172.16.253.38:9093,172.16.253.38:9094 --topic my-replicated-topic
集群消息的消费
分区消费组消费者的细节
HW和LEO
LEO 是某个副本最后消息的消息位置(log-end-offset),HW 是已完成同步的位置。消息在写入 broker 时,且每个 broker 完成这条消息的同步后,HW 才会变化。在这之前,消费者是消费不到这条消息的,在同步完成之后,HW 更新之后,消费者才能消费到这条消息,这样的目的是防止消息的丢失
kafka线上问题优化
1.防止消息丢失
生产者:使用同步发送;把ack设成1或者all,并且设置同步的分区数>=2
消费者
把自动提交改成手动提交
2.防止重复消费
如果生产者发送完消息后,却因为网络抖动,没有收到 ack,但实际上 broker 已经收到了。此时生产者会进行重试,于是 broker 就会收到多条相同的消息,而造成消费者的重复消费
解决方案:
- 生产者关闭重试,但会造成丢消息,不建议
- 消费者解决非幂等性消费问题,所谓非幂等性,就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,可以用唯一主键或分布式锁来实现
3.保证消息的顺序消费
生产者:使用同步发送,ack 设置成非 0 的值
消费者:主题只能设置⼀个分区,消费组中只能有⼀个消费者