Kafka简介和操作
消息引擎
消息引擎(消息中间件、消息队列):在消息传输过程中存储消息的容器
A —> 消息引擎 <---- B
消息引擎的好处
- 解耦
- 异步
- 削峰填谷
消息引擎的设计
-
消息协议
-
消息模型
-
点对点 A发送的消息只能是B获取
kafka中点对点的实现:一个分区只能被消费者组中的一个消费者消费
-
发布订阅 A发送的消息可以被多个用户获取
kafka中发布订阅的实现:kafka的主题/分区可以 被多个消费者消费
-
为什么不使用flume作为消息引 擎
1、channel中的数据一旦被sink,数据就没有了。不能指定存储周期。
2、flume增加消费者(sink)很麻烦,增加sink就需要增加channel,修改配置文件。
3、flume不能指定数据消费位置
flume的职责就非常简单了,仅仅就是做数据采集。
Kafka简介
官网地址:http://kafka.apache.org/
kafka是一个分布式的消息引擎和流平台。Kafka主要应用在实时流中。
kafka的相关概念(非常重要)
-
broker:kafka集群中的每一个节点就是broker
每个broker都有自己唯一的id号
-
controller: kafka集群也是主从结构的。主节点就是 controller。只是kafka的主节点不是配置的,而是选举的。
-
producer:生产者。就是往kafka分区写数据的
-
topic:主题。逻辑概念,就是对消息分类。
-
partition:分区。物理概念,数据存在磁盘上是按照 分区存储的。分区在磁盘上就是目录。目录的格式: topic-分区号;分区号从0开始的
-
replica:副本。kafka中的副本是在分区上的。
leader副本:只能有一个,数据的读写经过leader
follower副本:可以多个,同步leader副本的数据
leader副本挂掉,follower副本可以成为leader副本
-
offset:分区偏移量。生产者往分区中写入的每一条 信息都有一个索引编号(从0开始)对应。这个索引编号就是分区偏移量。分区偏移量是一串值。
-
consumer:消费者。从kafka分区中获取数据。
-
cunsumer offset:消费者偏移量。消费者偏移量是 一个值,是一个动态值。 消费者偏移量 = 消费的最 后一条数据的分区偏移量 + 1;
-
consumer group:消费者组。kafka的消费是以消费者组为单位的。
消费者偏移量是存在消费者组上的。
一个分区只能被消费者组中的一个消费者消费
一个消费者组中的消费者可以消费多个分区
-
group id:消费者组唯一id号。
Prodcucer的ACK应答机制
-
ack=0
producer生产数据不需要等待leader和follower的确认。
此时速度快,安全性低
此时配置重试无效
at most once
-
ack=1
这将意味着leader将把记录写入本地日志,但将在不等待所有follower的完全确认的情况下进行响应。
在这种情况下,如果leader在确认记录后立即失败,但follower还没有复制记录,那么记录就会丢失
at most once
-
ack=-1/all
这意味着leader将等待完整的同步副本来确认记录。
这保证了只要至少有一个同步副本仍然存在,记录就不会丢失。
这是最强有力的保证。这相当于acks=-1设置。
at least once
producer的exactly once
要实现exactly once,应该生产端和消费端同时保证。生产者如何做到exactly once??
exactly once = at least once + 幂等性
kafka从0.11开始新增加了生产者的幂等性配置。
exactly once:
-
ack=-1
-
enable.idempotence = true
以上的exactly once只能是单分区,单会话的精确一次。
已提交消息
消费者只能消费已提交的消息。
已提交:指的是生产者得到对应的ack成功应答的消息。
ISR
in sync replica
ISR是一个动态列表。leader天生就在ISR中。只有在ISR中的副本才有资格参与leader副本选举。
当follower副本同步leader副本数据时,落后了(replica.lag.time.max.ms =10000),follower副本就被踢出ISR。
无丢失消息的配置
1、不要使用 producer.send(msg),而要使用producer.send(msg, callback)。记住,一定要使用带有回调通知的 send 方法。
2、设置 acks = all。acks 是 Producer 的一个参数,代表了你对“已提交”消息的定义。如果设置成all,则表明所有副本 Broker 都要接收到消息,该消息才算是“已提交”。这是最高等级的“已提交”定义。
3、设置 retries 为一个较大的值。这里的retries 同样是 Producer 的参数,对应前面提到的 Producer 自动重试。当出现网络的瞬时抖动时,消息发送可能会失败,此时配置了 retries > 0 的Producer 能够自动重试消息发送,避免消息丢失。
4、设置 unclean.leader.election.enable =false。这是 Broker 端的参数,它控制的是哪些Broker 有资格竞选分区的Leader。如果一个Broker 落后原先的 Leader 太多,那么它一旦成为新的 Leader,必然会造成消息的丢失。故一般都要将该参数设置成 false,即不允许这种情况的发生。
5、设置 replication.factor >= 3。这也是Broker 端的参数。其实这里想表述的是,最好将消息多保存几份,毕竟目前防止消息丢失的主要机制就是冗余。
6、设置 min.insync.replicas > 1。这依然是Broker 端参数,控制的是消息至少要被写入到多少个副本才算是“已提交”。设置成大于1 可以提升消息持久性。在实际环境中千万不要使用默认值 1。
7、确保 replication.factor >min.insync.replicas。如果两者相等,那么只要有一个副本挂机,整个分区就无法正常工作了。我们不仅要改善消息的持久性,防止数据丢失,还要在不降低可用性的基础上完成。推荐设置成replication.factor = min.insync.replicas+ 1。
8、确保消息消费完成再提交。Consumer 端有个参数enable.auto.commit,最好把它设置成 false,并采用手动提交位移的方式。
Producer的流程
删除Topic流程
kafka高吞吐量的原因
- 顺写磁盘
- 使用操作系统的pagecahce
- 使用了操作系统(sendfile)零拷贝技术(zerocopy)
kafka集群容量的规划
假设每天kafka的数据量是1亿条(每条数据大小2kb),现在需要设计多大的集群容量呢?
考虑因素:
- 单条数据大小
- 数据量大小
- 副本数
- 保存周期
- 压缩
2 * 100000000 = 200000000KB
一天的数据大小:200GB ;需要预留10-20%的空间给索引文件
200G + 40G = 240G
kafka会对数据进行压缩的,压缩比75%左右
240G * 75% = 180G
3个副本的数据:180G
7天的总和 :180G * 7 = 1260G
3个副本: 1260G * 3 = 3780G
kafka副本同步
HW和LEO
HW和LEO的更新时机
副本正常同步过程
注意:副本同步可能会出现问题,当leader向follower发送hw时,如果follower挂了,follower的hw还是为0,重启follower后,leader又挂了,这时leader再重启就会成为follower,会向此时的leader同步数据,数据就会出现问题,因为hw会从1又变回0.
kafka将数据和日志分开
只需要将log.dirs的目录名不要写成logs,在server.properties配置文件里设置
kafka输出日志的目录就是固定logs
log.dirs=/home/hadoop/apps/kafka/logs
kafka的相关操作
启动kafka
kafka没有提供群启命令。需要自己写群启脚本
[hadoop@hadoop101 bin]$ kafka-server-start.sh
USAGE: /home/hadoop/apps/kafka/bin/kafkaserver-start.sh [-daemon] server.properties
[--overrideproperty=value]*
[hadoop@hadoop101 bin]$ kafka-server-start.sh -daemon ../config/server.properties
kafka集群群启动(关闭)脚本
kafka.sh
#! /bin/bash
# kafka集群群启动(关闭)脚本
##获取当前的用户名
USERNAME=$(whoami)
case $1 in
"start"){
for HOST in `cat /home/hadoop/script/nodes`
do
echo "Starting kafka on $HOST ..."
ssh $USERNAME@$HOST "source /etc/profile;/home/hadoop/apps/kafka/bin/kafka-server-start.sh -daemon /home/hadoop/apps/kafka/config/server.properties"
done
};;
"stop"){
for HOST in `cat /home/hadoop/script/nodes`
do
echo "Shutdown kafka on $HOST ..."
ssh $USERNAME@$HOST "source /etc/profile;/home/hadoop/apps/kafka/bin/kafka-server-stop.sh"
done
};;
esac
创建topic
[hadoop@hadoop101 bin]$ kafka-topics.sh
Create, delete, describe, or change a topic.
[hadoop@hadoop101 bin]$ kafka-topics.sh --create --topic goods --zookeeper hadoop101:2181,hadoop102:2181,hadoop103:2181/kafka --partitions 2 --replicationfactor 3
Created topic "goods".
删除topic
[hadoop@hadoop101 config]$ kafka-topics.sh --delete --zookeeper hadoop101:2181,hadoop102:2181,hadoop103:2181/kafka --topic goods
查看topic描述信息
[hadoop@hadoop101 config]$ kafka-topics.sh --zookeeper hadoop101:2181,hadoop102:2181/kafka --topic goods
--describe
更改topic的分区数
[hadoop@hadoop101 logs]$ kafka-topics.sh --zookeeper hadoop101:2181,hadoop102:2181/kafka --topic goods
--alter --partitions 3
生产数据
kafka提供了生产者的模拟客户端。
[hadoop@hadoop101 bin]$ kafka-console-producer.sh --topic goods --broker-list hadoop101:9092,hadoop102:9092,hadoop103:9092
消费数据
kafka提供了消费者的模拟客户端。
[hadoop@hadoop102 goods-1]$ kafka-console-consumer.sh --zookeeper hadoop101:2181,hadoop102:2181,hadoop103:2181/kafka --topic goods --from-beginning
[hadoop@hadoop102 goods-1]$ kafka-console-consumer.sh --bootstrap-server hadoop101:9092,hadoop102:9092,hadoop103:9092 --topic goods
##__consumer_offsets-分区号:50个分区 将offset偏移量存储在kafka自身
## 使用消费者gid.hashCode % 50 得到的就是__consumer_offsets主题的分区号
注意:
offset存储在zk的好处和弊端:
- 好处:方便监控
- 弊端:zk不适合大批量的频繁写入数据
kafka存储offset是存在__consumer_offsets主题中的。kafka中分区的数据默认保留7天。所以offset可能出现过期,此时可以借助外部数据库存储offset,推荐使用redis。
offset在zk上的路径
/kafka/consumers/消费者组/offsets/主题/分区
查看log数据文件和index索引文件
[hadoop@hadoop101 infos-0]$ kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.log
[hadoop@hadoop101 infos-0]$ kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.index
## 查看数据详细信息
[hadoop@hadoop102 goods-0]$ kafka-run-class.sh kafka.tools.DumpLogSegments --files 00000000000000000000.log --deep-iteration --print-data-log
## 索引项<相对偏移量,position>
## 索引项占据大小 8字节 ,相对偏移量占4字节(int),position占4个字节
## 但是 kafka中的offset是长整型long。long类型8字节
offset: 17 position: 5233
offset: 25 position: 9664
offset: 33 position: 14035
offset: 41 position: 18987
offset: 51 position: 23374
offset: 62 position: 27769
00000000000000000000.log – offset 0 – basicOffset-- 基准偏移量
00000000000000000000.index 相对偏移量 == offset - 基准偏移量
00000000000000000100.log – offset 100 --basicOffset – 基准偏移量
00000000000000000100.index offset: 17 position: 52330
Log数据分段的触发条件
- 分段日志的大小达到1G
- 索引文件达到10M
- 分段日志中消息的最大时间戳达到7天
- 当前消息的offset - 基准offset > Integer.MAX_VALUE
kafka的数据文件
kafka消息传递的三种语义
- at least once(至少一次语义):消息至少消费一次,数据不丢失,但是数据可能重复
- at most once(最多一次语义):消息最多消费一次,数据不重复,但是数据可能丢失
- exactly once(精确一次语义):数据不重复不丢失。— 很难做到。
要实现exactly once,就需要实现幂等性( Idempotent ):无论执行多少次,最终结果 一样。
注意:at least once + 幂等性 可以实现精确一次,但只是保证单分区,多分区之间数据依然可能会重复。
如何根据offset查找信息
假如现在index文件的数据如下:
offset: 17 position: 5233
offset: 25 position: 9664
offset: 33 position: 14035
offset: 41 position: 18987
offset: 51 position: 23374
offset: 62 position: 27769
现在要查找offset=21的消息,如何查找??
1、根据offset的值是21,可以定位到索引文件是00000000000000000000.index
2、使用offset的值减去对应的基准偏移量(21- 0)
3、使用二分查找在00000000000000000000.index文件中找到小于21的最大值,这里就是17
4、根据17对应的物理位置5233,在00000000000000000000.log中定位都该数据,然后按照顺序向后查找到21对应的数据。
指定分区进行消费
[hadoop@hadoop102 ~]$ kafka-console-consumer.sh --bootstrap-server hadoop101:9092,hadoop102:9092,hadoop103:9092 --topic goods --partition 0
kafka的消息是分区内有序,全局是的顺序是kafka无法保证的。
指定offset消费数据
[hadoop@hadoop102 ~]$ kafka-console-consumer.sh --bootstrap-server hadoop101:9092,hadoop102:9092,hadoop103:9092 --topic goods --partition 0 --offset 3
消费者组
## 修改消费者组
[hadoop@hadoop102 config]$ vim consumer.properties
bootstrap.servers=hadoop102:9092
# consumer group id
group.id=test
## 使用消费者组,不能带上分区,kafka存储offset
[hadoop@hadoop103 config]$ kafka-console-consumer.sh --bootstrap-server hadoop101:9092,hadoop102:9092,hadoop103:9092 --topic goods --consumer.config consumer.properties
## 使用zk存储offset
[hadoop@hadoop103 config]$ kafka-console-consumer.sh --zookeeper hadoop101:2181,hadoop102:2181,hadoop103:2181/kafka --topic goods --consumer.config consumer.properties
查看log数据
[hadoop@hadoop101 infos-0]$ kafka-runclass.sh kafka.tools.DumpLogSegments --files 00000000000000000000.log --printdata-log --deep-iteration
查看offset
## 查看kafka自身的offset
[hadoop@hadoop101 bin]$ kafka-consumer-groups.sh --bootstrap-server hadoop101:9092,hadoop102:9092,hadoop103:9092 --group test --describe
## 查看zk上的offset
[hadoop@hadoop101 bin]$ kafka-consumer-groups.sh --zookeeper hadoop101:2181,hadoop102:2181,hadoop103:2181/kafka --group test --describe
查看消费者组
## 查看kafka自身的消费者组
[hadoop@hadoop101 bin]$ kafka-consumer-groups.sh --bootstrap-server hadoop101:9092,hadoop102:9092,hadoop103:9092 --list
## 查看zk的消费者组
[hadoop@hadoop101 bin]$ kafka-consumer-groups.sh --zookeeper hadoop101:2181,hadoop102:2181,hadoop103:2181/kafka --list
分区副本平衡
[hadoop@hadoop101 bin]$ kafka-preferred-replica-election.sh --zookeeper hadoop101:2181,hadoop102:2181,hadoop103:2181/kafka