客户端参数
生产者
acks=0或1或-1或all
0 : 不关心消息是否持久化
1 :消息必须持久化并返回
-1或者all :所有副本成功,如果配置了min.insync.replicas就是执行副本同步成功才返回成功
RETRIES_CONFIG: 重试此处
RETRY_BACKOFF_MS_CONFIG: 重试间隔
BUFFER_MEMORY_CONFIG: 消息缓冲区大小,消息先放在buffer中然后每16k拉取
LINGER_MS_CONFIG: 如果消息不足16k 但是到了设置时间 就按设置时间发送
消费端
消费者发起拉取,在时间窗口中反复拉取 如果拉到则直接返回,如果没有拉取到则重新拉取并计算是否大于时间窗口,如果大于时间窗口则直接返回为空
KEY_DESERIALIZER_CLASS_CONFIG|VALUE_DESERIALIZER_CLASS_CONFIG:序列化和反序列化
订阅主题
consumer.subscribe(Arrays.asList(TOPIC_NAME));
是否自动提交,客户端返回MQ offset
自动提交的时间 一般不会自动提交 需要手动批量提交
ENABLE_AUTO_COMMIT_CONFIG
AUTO_COMMIT_INTERVAL_MS_CONFIG
consumer.commitSync()
心跳最大间隔时间 超过时间则集群认为消费者有问题踢出集群
HEARTBEAT_INTERVAL_MS_CONFIG : 每次心跳间隔时间 默认3s
SESSION_TIMEOUT_MS_CONFIG :心跳异常最大容忍时间
每次拉取消息最大数
MAX_POLL_RECORDS_CONFIG
两次poll间隔最大容忍时间,超过时间则kafka就认为消费者性能太弱提出集群
这个配置一般不要随意调整 可以调整每次拉取任务数来控制
MAX_POLL_INTERVAL_MS_CONFIG
每次新增分组的时候默认是读取新的消息 此时offset为0
如果想消费之前的消息则修改设置为earliest
消费完之后已经有offset了下次启动就从次offset开始
默认为latest
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, “earliest”
指定消费
- 指定分区消费
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
- 消息全部回溯
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
consumer.seekToBeginning(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
- 指定offset消费
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
consumer.seek(new TopicPartition(TOPIC_NAME, 0), 10);
- 指定时间消费 先调用Api提供的根据时间查询offset后自己根据offset查询
List<PartitionInfo> topicPartitions = consumer.partitionsFor(TOPIC_NAME);
Kafka日志
分段存储
根据log.segment.bytes设置文件大小
索引文件
存储每4k消息中最小的offset
查询的时候使用二分法先查索引文件
时间索引
索引和发送消息的时间戳
集群设计
总控制器Controller
所有的broker会选取一个Controller
职责:
- 分区leader异常重新选举
- 分区ISR集合变化,通知所有broker更新元信息
- 增加分区时,通知其他节点感知
选举机制
谁先启动谁就是Controller
同时启动则同时执行zk create controller(临时节点)
创建成功的则为Controller
如果Controller挂了重新 create controller(监听)
Partition选举
controller监听 /broker/ids节点内容
如果发生变化
- leader挂了 则直接读取ids中的 Isr(已同步列表)中第一个节点为新的leader
- 如果ISR列表为空了 则读取 unclean.leader.eletion.enable=true则可以用ISR意外的服务参加选举,这样提高了可用性 但是会丢消息
offset记录机制
每个consumer提交消费分区给kafka内部topic:_consumer_offset
内容中key为consumerGroupId + topic + 分区号
内容value就是当前的offset值
内部topic分区 默认50个
存储规则为 hash(consumerGroupId) % _consumer_offset 分区数
Rebalance
每个消息只有group中的一个消费者消费
其实是通过每个partition都只绑定group中一个服务实现的 所以也是固定的
但是如果消费者挂了partition和消费者之间需要重新建立连接
这个过程就是rebalance
会发生rebalance的情况:
- 消费者增加或减少
- topic增加了分区
- 消息组订阅了新的topic
rebalance影响性能注意
过程
- 消费者调用kafka选出kafka 组长(GroupCoordinator)
组长负责监控消费组内所有消费者的心跳,判断是否挂机、是否举行rebalance - 第一个调用kafka 组长的消费者就是 消费者组长(leaderCoordinator)
这个 消费组长负责指定分区方案 - 分区方案 同步给 kafka组长 在其他消费者ping的时候同步给各个消费者
策略
partition.assignment.strategy控制
range策略
假设一个主题有10个分区(0-9),现在有三个consumer消费:
range策略就是按照分区序号排序,假设 n=分区数/消费者数量 = 3, m=分区数%消费者数量 = 1,那么前 m 个消费者每个分配 n+1 个分区,后面的(消费者数量-m )个消费者每个分配 n 个分区。
round-robin策略
轮询分配,
比如分区0、3、6、9给一个consumer,分区1、4、7给一个consumer,分区2、5、8给一个consumer
sticky策略
初始时分配策略与round-robin类似,
但是在rebalance的时候,需要保证如下两个原则。
1)分区的分配要尽可能均匀 。
2)分区的分配尽可能与上次分配的保持相同。(其余两个为整体rebalance)
HW与LEO详解
HW
取一个partition对应的ISR中最小的LEO(log-end-offset),consumer最多只能消费到HW所在的位置。
leader写入新的消息不会立即给consumer消费
需要ISR中的从节点同步数据并更新自身HW,所有的更新完了
消息才会被消费者消费
这个就是保证了消息幂等性