目录
架构
kafka的原理
生产者原理
发送消息
服务端响应ACK
Kafka Broker存储原理
partition分区
replica副本
segment
索引
消息保留和清理机制
消费者原理
offset的维护
offset的跟新
消费者消费策略(消费者与分区的关系)
RangeAssignor(默认)
RoundRobinAssigner(轮询)
StickyAssignor(粘滞)
架构
- Topic 生产者指定topic消息发送到kafka,消费者拉取指定的topic
- Partition topic之下的分区,用于将消息进行分流
- Replica 副本,在集群环境中,用于消息的备份,实现数据的高可用
- segment Partition之下的切分,kafka的数据是存放在*.log文件中,如果数据量越来越大,那么检索效率会越来越低,所以以段(segment)的方式来存储,默认大小1G
- Consumer Group 消费者组,消费同一个topic的消费者不一定是同一个组。
- Consumer offset 偏移量,kafka的数据是顺序写入,顺序拉取的,offset记录下一条将要发送给消费者的数据
- latest和earliest区别 : earliest 当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费。latest 当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
3台Broker。两个Topic:Topic0和Topic1。Topic0有2个分区:partition0和partition1,每个分区一共3个副本。Topic1只有1个分区:partition0,每个分区一共3个副本。图中红色字体的副本代表是leader,黑色字体的副本代表是follower。绿色的线代表是数据同步。蓝色的线是写消息,橙色的线是读消息,都是针对leader节点。有两个消费者组,第一个消费者组,消费了topic0的两个分区。第二个消费者组,既消费topic0,又消费topic1。其中有一个消费者,消费topic0的partition0,还消费topic1的partition0。有一个消费者,消费partition0的partition1。有一个消费者,没有partition可以消费。
kafka的原理
生产者原理
发送消息
生产者主要由两个线程协调运行,分别是main线程和sender线程。
在创建KafkaProducer时,会创建一个sender对象,并启动一个IO线程
拦截器:实现消息的定制化。
序列化:调用send方法后,利用指定的工具对消息进行序列化
路由指定(分区器):有四种情况。
1、指定了partition——用它;
2、没有指定partition,自定义了分区器——按自定义的规则来;
3、没有指定partition,没有自定义分区器,但是key不为空——hash以后取余;
4、没有指定partition,没有自定义分区器,但是key是空的——整数自增取模。
服务端响应ACK
- acks=0:producer不等待broker的ack,这种情况延迟最低,broker一接受到还没有返回磁盘就已经返回,当broker故障时会丢失数据。
- acks=1(默认): producer等待broker的ack,leader(主节点)落盘成功后返回ack,如果在follower同步成功前leader故障,那么数据会丢失。
- ack=-1(all):producer等待broker的ack,leader和follwer全部落盘成功后返回ack。但是如果在follower同步完成后,broker发送ack之前,leader发生故障,没有给生产者发送ack,那么producer会重新发送数据,造成数据重复。解决方案:将reties设置为0(不重发)。
Kafka Broker存储原理
配置文件:config/server.properties
默认日志路径:/tmp/kafka-logs
partition分区
横向扩展,将数据放在不同的broker上,降低broker访问压力,一个topic中的数据可分为多个partition,一个partition中的数据是有序的,但全局不一定有序。日志目录中topic名后面的数字代表分区、
replica副本
副本机制是为了提高分区的可靠性,实现数据的高可用。
副本因子不能大于Broker的个数;
第一个分区(编号为0)的第一个副本放置位置是随机从 brokerList 选择的;
其他分区的第一个副本放置位置相对于第0个分区依次往后移
segment
为防止log不断追加导致文件过大,导致检索消息效率变低,一个partition又被划分为多个segment来组织数据。每个segment以下图成套出现。
.log文件:
- 第一种,当文件大小大到某个临界值时,会创建一个新的log文件(segment),用最新的offset作为名称。segment默认大小1G。参数设置:log.segment.bytes
- 第二种,根据时间戳,默认168个小时。log.roll.hours=168
.index:偏移量索引文件 .timeindex:时间戳索引文件
索引
.index偏移量索引文件记录的是offset和消息物理地址(在log文件中的位置)的映射关系
使用bin目录下dumplog工具查看索引文件
这是一种稀疏索引,产生n条消息记录一条索引。默认是产生4KB消息后增加一条消息记录。参数控制:log.index.interval.bytes=4096。
总结:一个topic有多个partition,一个partition有多个segment,segment中有.log(消息数据)、.index和.timeindex文件(索引)
消息保留和清理机制
开关策略:开关默认是开启的 log.cleaner.enable=true。 log.cleanup.policy=delete/compact(删除/压缩)
删除策略:
- log.retention.check.interval.ms=300000 5分钟执行一次,看是否有需要删除的日志。
- log.retention.hours=168 默认168小时(一周) log.segment.bytes(segment文件大小)超过后删除 log.retention.bytes(总文件大小)
消费者原理
offset的维护
offset是一个存放在名为_consumer_offset的特殊topic中,默认50个分区,每个分区默认一个replication。
_consumer_offset的大致数据结构如下图所示
通过哈希取模可得到一个consumer group的offset放在这个特殊topic的哪个分区。
offset的跟新
消费者组的offset是保存在broker的,但是由消费者上报给broker,但并不是一消费消息,offset就会跟新,消费者必须要有一个commit的动作(ack)。
消费者可以通过参数控制是自动提交还是手动提交:enable.auto.commit=ture/false
如果我们需要在消费完消息做完业务逻辑后才commit,就要把这个值改为false,消费者必须调用一个方法让broker更新offser。方式有二:
消费者消费策略(消费者与分区的关系)
RangeAssignor(默认)
RangeAssignor策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。
RoundRobinAssigner(轮询)
StickyAssignor(粘滞)
原则:
1、分区的分配尽可能的均匀
2、分区的分配尽可能的和上次分配保持相同