参考:kafka官方文档、javaguide
持续更新,需要的可以关注收藏一下。
本文以kraft之后的版本为准,不讲解zookeeper相关的部分(大概也没有多少公司还会使用和zookeeper绑定的版本了...)
这里也说句题外话,如果你是消息队列的初学者,正在纠结要先学哪种消息队列,比如RabbitMQ、Kafka、RocketMQ、ActiveMQ、Pulsar等,我推荐你先学Kafka。有以下几点原因:
1、国内的大部分企业现在所使用的消息队列选型都是Kafka,其次是RocketMQ。其他的MQ在国内企业使用的占比都比较低,比如RabblitMQ,它和Pulsar的占比大致相当(Pulsar也有可能是未来的方向,这点有兴趣的可以自行了解一下)。至于ActiveMQ则已是过时技术,不推荐学习。
2、对于初学者来说,Kafka的入门难度是最低的,因为它相对其他MQ来说,只是将其单纯作为消息队列来看的话,它的设计是更简洁易懂的,功能上更纯粹(比如它缺少事务消息、延迟消息等功能),并且其架构设计也是十分优秀的,从RocketMQ的架构借鉴了Kafka的这点就足以证明,如果你学习过RocketMQ的话,你会发现这两者在设计上极其相似。所以如果你学过Kafka,当公司的选型是RocketMQ时,大概率你也能够很快上手。
3、生态丰富,适配性也好,如果你在使用kafka时遇到什么问题,网上会有相当多的文章和文档提供解决方案。
4、性能最好
Kafka核心概念
什么是Producer、Consumer、Broker、Cluster、Topic、Partition?
- Producer:生产者,生产消息的一方。
- Consumer:消费者,消费消息的一方。
- Broker:代理,指的是一个独立的kafka服务实例,负责消息的接收、存储和发送。多个Broker组成一个Cluster。
- Topic:主题,Producer发送消息的时候会指定一个Topic,并发送到这个Topic下,Consumer通过订阅该Topic消费消息。
- Partition:分区,消息实际存储的地方,实际上就是kafka中真正的消息队列,一个partition可以存储多个消息,消息先进先出。一个Topic可以有多个Partition,当Producer发送消息时,实际上是发送到指定的Topic下的某个Partition。同一个Topic下的Parition可以分布在不同的Broker下,也就是说,同一个Topic是可以横跨多个Broker的。
整体架构图如下:
Kafka的多副本机制?
Kafka的Partition是有多副本机制的,一个Partition会有一个或多个副本(Replica),其中的一个称之为leader,其他副本称为follower。也就是说,如果一个Parition只有自己一个,没有follower,它自己就是主副本(leader replica)。
生产者发送消息时,会把消息发送到leader副本,然后follower副本才会从leader副本拉取消息进行同步。follower副本是leader副本的拷贝,生产者和消费者只会和leader副本进行交互,因为follower副本的存在只是为了保证消息存储的安全性。leader副本和follower副本会分布在不同的Broker上,当leader副本发生故障时(比如说leader副本所在的broker挂掉了),会从follower副本中选举出一个新的leader,但是只有在ISR中的副本才能够参加选举。
ISR、LEO、HW的含义?
ISR:In-Sync Replicas,在同步中的副本集合,包含了leader副本和所有可用且消息量与leader相差不多的follower副本,它是整个副本集合的一个子集。
生产者发送消息时,会把消息发送到leader副本,然后follower副本才会从leader副本拉取消息进行同步。follower副本同步的时候会产生一定的延迟,导致 follower 副本中保存的消息略少于 leader 副本,但是只要没有超出配置的一些参数阈值(比如相差的消息不超过5条等),这些follower副本就会一直存在于ISR中。但是如果一个 Follower 副本出现异常,比如宕机、网络断开等原因长时间没有同步到消息,那这个时候,Leader就会把它移出ISR。
在Kafka中,一个副本存在于ISR中,需要满足一定条件:
- 该副本为leader副本;
- 超过
replica.lag.time.max.ms配置项配置的时间间隔,一直没有同步到leader副本的最新消息,或者比leader副本落后了replica.lag.max.messages以上的消息条数时,该副本就会被剔除出ISR。(ps:replica.lag.max.messages参数在新版本kafka中已经标注为过时)
LEO:日志末端偏移量 (Log End Offset),记录该Partition中下一条消息的偏移量,举个例子,如果LEO=10,那么表示该副本只保存了偏移量值是[0, 9]的10条消息;
HW:高水位(High Watermark),它代表一个偏移量offset信息,表示消息的复制进度,指的是消息已经成功复制到哪个位置了,在HW之前的所有消息都已经被成功写入到所有副本中,因此,消费者可以安全地消费这些已成功复制的消息。
对于一个Parttion而言,小于等于HW值的所有消息都被认为是已备份的,消费者只能拉取到这个offset及其之前的消息。
一图胜千言:
Kafka生产问题
如何保证消费顺序?
当生产者发送消息到某个Topic之前,会先选择一个partiton进行发送,然后kafka会采用尾加法将该消息放置在partition的尾部,消费者也会按照partition中消息原本的顺序进行消费。所以,Partition中的消息是可以保证有序的。
因此,有以下两种常用的方式:
- 一个Topic只配置一个Parition(不推荐)
- 发送消息的时候指定partiton或者key
不推荐第一种方式的原因在于,Topic中的多partition设计的初衷就是为了提高并发,显然这种方式违背了初衷,也降低了系统的并发量。
生产者发送消息之前,可以直接指定partiton,也可以指定消息的key从而间接指定partiton。如果不指定partition的话,发送消息默认会采用分区轮询策略,也就是第一次发消息发到partition0,第二次发到partition1...以此类推。如果指定了partition,就会发送到对应的partition。如果指定了key,就会根据key做一系列运算得到对应的partition,并发送到该partition,所以相同的key就会被发送到同一个partition中。这部分涉及分区负载均衡策略,有兴趣可以自行了解,后续也可能会补充在这篇文章中。
如何保证消息不丢失?
生产者丢失消息
生产者在调用send方法发送消息之后,消息可能会因为网络故障而没有发送到kafka,这种情况下消息就丢失了。
所以,我们不能默认在调用send方法发送消息之后消息发送成功了。为了确定消息是发送成功,我们要判断消息发送的结果。但是要注意的是 Kafka 生产者(Producer) 使用 send 方法发送消息实际上是异步的操作,我们可以通过 get()方法获取调用结果,但是这样也让它变为了同步操作,示例代码如下:
SendResult<String, Object> sendResult = kafkaTemplate.send(topic, o).get();
if (sendResult.getRecordMetadata() != null) {
logger.info("生产者成功发送消息到" + sendResult.getProducerRecord().topic() + "-> " + sendRe
sult.getProducerRecord().value().toString());
}
在生产环境不推荐这么做,因为这违背了使用消息队列的初衷之一,也就是异步。 一般采用为其添加回调函数的形式:
ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, o);
future.addCallback(result -> logger.info("生产者成功发送消息到topic:{} partition:{}的消息", result.getRecordMetadata().topic(), result.getRecordMetadata().partition()),
ex -> logger.error("生产者发送消失败,原因:{}", ex.getMessage()));
(ps:这里是以java8为例,在java17及之后都由ListenableFuture改为了CompletableFuture,这里只是提供了一种思维,具体参考实际的api,思维是不变的)
同时也要为Producer配置retries配置项,这个配置项指的是生产者发送消息失败后会重试的次数,设置完成后,当消息发送失败后,生产者会自动重发消息。另外还应配置重试等待时间参数retry.backoff.ms和 retry.backoff.max.ms ,如果等待时间太小的话,重试的效果就不明显了,因为重试次数可能会在短暂的网络故障区间内迅速用完。
kafka丢失消息
当某个Partiton的所有副本都因为某些原因,所在的broker都故障或是宕机了,该partition下的消息的就丢失了。
kafka通过多副本机制保证消息存储安全性,但是基于此,还需要做一些其他的配置进一步保证消息存储安全性。
- 配置 acks = all:这个配置项的含义是当消息成功写入到当前partition的多少个副本之后,kafka才会返回响应。acks 的默认值即为 1,代表我们的消息被 leader 副本接收之后就算被成功发送。当我们配置 acks = all 表示只有ISR 列表中所有的副本全部收到消息时,生产者才会接收到来自服务器的响应。这种模式是最安全的级别,但是由于要等待所有follower都完成同步之后才会收到响应,所以该模式的延迟会很高。
- 配置 replication.factor >= 3:这个配置的含义是,该Topic下的每个partition有多少个副本。为了保证 leader 副本能有 follower 副本能同步消息,我们一般会为 topic 设置 replication.factor >= 3。这样就可以保证每个partition至少有 3 个副本。虽然造成了数据冗余,但是带来了数据的安全性。
- 配置 min.insync.replicas > 1:这个配置的含义是,当生产者配置了
acks = all时,ISR列表中至少要有多少个副本,kafka才会返回写入成功。一般情况下我们还需要设置min.insync.replicas> 1,这样配置代表消息至少要被写入到 2 个副本才算是被成功发送。min.insync.replicas 的默认值为 1 ,在实际生产中应尽量避免默认值 1。但是,为了保证整个 Kafka 服务的高可用性,你需要确保 replication.factor > min.insync.replicas 。为什么呢?设想一下假如两者相等的话,只要是有一个副本挂掉,整个分区就无法正常工作了。这明显违反高可用性!一般推荐设置成 replication.factor = min.insync.replicas + 1。 - 配置 unclean.leader.election.enable = false:这个参数的含义是,在leader副本故障时,是否允许不在ISR列表中的follower副本作为兜底手段被选为leader。配置了
unclean.leader.election.enable = false的话,当 leader 副本发生故障时就不会从 follower 副本中和 leader 同步程度达不到要求的副本中选择出 leader ,这样降低了消息丢失的可能性。
以上参数的详解可以参见下文的配置详解部分。
消费者丢失消息
当消费者拉取到分区的某个消息之后,自动提交了offset,但是在消费完成之前,消费者宕机了,导致消费失败,此时这个消息就丢失了。 解决方法就是关闭自动提交offset,每次在消费完成之后手动提交offset。
如何保证消息不重复消费?
出现重复消费的原因:
- 消费者成功消费了消息之后,提交offset失败。(比如说处理了一条订单数据之后,提交了事务,但是在提交offset时突然宕机)
- 消费者消费时间过长,或是网络原因,导致kafka以为消费者掉线或是宕机,触发了partition rebalance。(比如说partition1原本是交给consumer1进行消费的,consumer1在消费一条消息之后,由于网络故障,kafka没有收到ack,从而无法成功提交offset,并且也长时间没有收到consumer1的心跳,从而认为consumer1已经宕机,partition rebalance后将partition1交给consumer2进行处理,consumer2就会消费到刚刚consumer1已经消费但没提交offset的那条消息,从而造成了重复消费)
解决方案:
- 消费者端消费消息之前,对消息做幂等性校验。比如使用Redis的setnx、MySQL的主键或者唯一索引等幂等功能。
- 同时,将消费者端的
enable.auto.commit设置为false,改为消费完成之后提交手动提交offset。为什么选择消费完成之后提交手动提交offset呢?如果拉取到消息就提交的话,就会有消息丢失的风险。
Kafka消费者重试机制
以springboot项目为例。
Kafka 消费者在默认配置下会进行最多 10 次 的重试,每次重试的时间间隔为 0,即立即进行重试。如果在 10 次重试后仍然无法成功消费消息,则不再进行重试,消息将被视为消费失败。
如何修改这些配置呢,在springboot项目下可以使用@RetryableTopic注解,示例如下:
package com.example.demo.kafka;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.DltHandler;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.annotation.RetryableTopic;
import org.springframework.kafka.retrytopic.Backoff;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class OrderConsumer {
// 👇 主监听器 + 重试配置
@RetryableTopic(
attempts = "4", // 总共尝试 4 次(1次原始 + 3次重试)
backoff = @Backoff(
delay = 1000, // 初始重试间隔,初始延迟,单位为ms
multiplier = 2.0, // 倍数因子(用于指数退避),例如 multiplier=2,则重试间隔为:2s → 4s → 8s → ...
maxDelay = 10000, // 最大延迟上限(毫秒),例如这里,重试间隔到了10秒就不会再增加
random = false // 是否添加随机抖动,防止多个消费者实例在同一时刻重试
),
autoCreateTopics = "true" // 自动创建 retry 和 dlt 主题(开发环境可用)
)
@KafkaListener(topics = "order-topic", groupId = "order-group", concurrency = "3")
public void listenOrder(ConsumerRecord<String, String> record) {
String value = record.value();
log.info("【正常消费】收到订单消息: {}", value);
// 👇 模拟业务处理失败(始终抛异常)
if (true) {
throw new RuntimeException("订单处理失败,触发重试!");
}
// 正常逻辑(不会执行到)
log.info("订单处理成功: {}", value);
}
// 👇 死信队列处理器
@DltHandler
public void handleDlt(ConsumerRecord<String, String> record) {
String topic = record.topic();
String key = record.key();
String value = record.value();
log.error("【死信处理】消息进入 DLT,topic={}, key={}, value={}", topic, key, value);
// TODO: 可在此处做以下操作:
// - 记录到数据库(人工干预)
// - 发送告警邮件/钉钉
// - 转发到其他补偿系统
}
}
这里只是一个示例,实际使用最好把这些参数放在配置文件内。
Kafka配置详解
spring-kafka详解
spring-kafka对kafka的生产者客户端和消费者客户端都做了高度的封装和抽象,尤其是消费者端的KafkaListener。
示例配置如下:
spring:
kafka:
bootstrap-servers: localhost:9092 # 用来初始化连接kafka(不用配置全部节点,会动态发现)
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
buffer-memory: 33554432 # 缓存容量。默认值32MB = 33554432
batch-size: 163840 # 默认 single request 批处理大小(以字节为单位),默认16KB = 16384
retries: 1 # 消息发送失败重试次数
acks: 1
properties:
linger:
ms: 500 # 不是立即发送一条记录,producer将会等待给定的延迟时间以允许其他消息记录发送。与batch-size配合使用,满足一个就发送
max:
request:
size: 1048576 # 请求的最大字节数
consumer:
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
group-id: radar # 默认消费者组
max-poll-records: 2000 # 批量一次最大拉取数据量
enable-auto-commit: false # 自动提交已消费offfset,false-禁用
auto-commit-interval: 4000 # 自动提交时间间隔,单位ms
auto-offset-reset: earliest
heartbeat-interval: 10000 # ⼼跳与消费者协调员之间的预期时间(以毫秒为单位)
fetch-max-wait: 500
listener:
ack-mode: manual_immediate # manual_immediate-手动ack后立即提交;batch-批量自动确认;RECORD-单条自动确认;
type: batch # 批量消费
missing-topics-fatal: false # 未发现topic时不报错: 自动创建topic需要设置为false
template:
default-topic: radar
patitions: 7
replications: 1
生产者端不做过多叙述,直接参考下文的Producer Configs即可。
简单讲讲消费者,这部分网上文章很少,我个人也是按照我对源码的理解写下的,如有误,可以在评论区指出。
消费者端中,spring-kafka新增了listener这个概念。
首先介绍一下KafkaMessageListenerContainer。一个springboot应用中可以有多个KafkaMessageListenerContainer实例,一个KafkaMessageListenerContainer实例分配Topic中的一个分区进行消费;
如果设置为1的情况下, 这一个实例消费Topic的所有分区;
如果设置多个,那么会平均分配所有分区;
如果实例>分区数; 那么空出来的实例会浪费掉;
如果实例<=分区数 那么会有一部分实例消费多个实例,但也是均衡分配的。
每一个KafkaMessageListenerContainer中,包含一个kafka Consumer。
Consumer只负责:
- 连接 Kafka 集群(connect)
- 订阅 Topic(subscribe)
- 拉取(poll)消息
- 提交 offset(commit)
KafkaMessageListenerContainer会调用Consumer的poll()函数去获取消息,然后交给@KafkaListener标记的方法(下文称为listener方法)去做实际的消费动作。
如果在在listener方法中手动调用ack,并不是说一调用ack之后就提交offset了,实际只是告知Consumer提交offset,如果listener的ack-mode配置为manual_immediate,Consumer就会在调用ack之后立刻提交offset,也就是立即调用CommitAsync函数。
由于spring-kafka的源码在每个版本的变更都不少,这里不去过多叙述,只要了解一下底层的概念即可,感兴趣的可以翻看一下源码。
Producer configs
生产者客户端的配置。
bootstrap.servers
客户端用于建立与Kafka 的 broker 集群的初始连接的主机/端口对列表。客户端使用此列表启动并发现完整的Kafka broker 集群。列表中 broker 的顺序无关紧要,但我们建议包含多台服务器,以确保在任意服务器宕机时仍能保持可用性。此列表无需包含全部 broker,因为Kafka客户端会自动管理和更新与集群的连接。此列表的格式必须为 host1:port1,host2:port2
官方文档原文:
A list of host/port pairs used to establish the initial connection to the Kafka cluster. Clients use this list to bootstrap and discover the full set of Kafka brokers. While the order of servers in the list does not matter, we recommend including more than one server to ensure resilience if any servers are down. This list does not need to contain the entire set of brokers, as Kafka clients automatically manage and update connections to the cluster efficiently. This list must be in the form host1:port1,host2:port2,....
key.serializer
消息在被发送之前,需要先序列化。这个配置用于指定消息的 key 的序列化器。 序列化器需要实现org.apache.kafka.common.serialization.Serializer接口。
value.serializer
这个配置用于指定消息的 value(也就是消息的主体部分,一般就是实际发送的内容) 的序列化器。 序列化器需要实现org.apache.kafka.common.serialization.Serializer接口。
retries
retries 配置项的含义是,当发送消息出现暂时的错误时的重试次数。当这个配置项大于 0 时,客户端会自动重新发送那些因为可能存在的暂时错误而发送失败的消息。简单来说,就是当消息发送失败时,客户端会重新发送的最大次数。客户端会重新发送直到发送成功,或者因为一个非暂时的错误而发送失败,或者达到delivery.timeout.ms 这个配置项配置的发送超时时间(这点后面会详细说到)。当这个配置项设置为 0 时,客户端在发送失败之后,不会重试,并且这个错误会传播到客户端程序(也就是生产者端)去进行处理。用户通常应该不设置此配置,而是使用 delivery.timeout.ms 来控制重试行为。
该配置项为一个整数,范围在 0 到整数的最大值,默认为整数的最大值。官方文档中指示该配置项重要性为高。
官方文档原文:
Number of times to retry a request that fails with a transient error. Setting a value greater than zero will cause the client to resend any record whose send fails with a potentially transient error. Requests will be retried this many times until they succeed, fail with a non-transient error, or the delivery.timeout.ms expires. Note that this automatic retry will simply resend the same record upon receiving the error. Setting a value of zero will disable this automatic retry behaviour, so that the transient errors will be propagated to the application to be handled. Users should generally prefer to leave this config unset and instead use delivery.timeout.ms to control retry behavior. Enabling idempotence requires this config value to be greater than 0. If conflicting configurations are set and idempotence is not explicitly enabled, idempotence is disabled. Allowing retries while setting enable.idempotence to false and max.in.flight.requests.per.connection to greater than 1 will potentially change the ordering of records because if two batches are sent to a single partition, and the first fails and is retried but the second succeeds, then the records in the second batch may appear first.
retry.backoff.ms
retry.backoff.ms的含义是在发送消息失败之后,重发消息之前需要等待的时间。这可以避免在某些失败场景下,在短时间内反复发送请求。此值是初始值,每一次重试失败之后,等待时间将呈指数级增长,直至达到 retry.backoff.max.ms 值。
官方文档原文:
The amount of time to wait before attempting to retry a failed request to a given topic partition. This avoids repeatedly sending requests in a tight loop under some failure scenarios. This value is the initial backoff value and will increase exponentially for each failed request, up to the retry.backoff.max.ms value.
acks
acks 配置项的含义是,生产者要求 leader 在返回 ack (成功响应)之前,需要收到多少个副本的 ack(ps:leader 也是一个副本,kafka 的概念中包含了 leader replica 和 follower replica,这个理解很重要)。这个配置项控制了发送出去的消息的持久性。这个配置项有以下的值:
● acks=0:当 acks=0 时,生产者发送消息之后不会等待 broker 返回的响应。消息会被立刻发送到生产者程序的 socket 缓冲区,并被生产者认为已经发送。在这个配置项下,无法保证 broker 收到消息,并且 retries配置项也会失效(因为生产者不会感知到发送失败)。每条消息的 offset 都会被设置为-1,因为生产者不会知道这条消息的 offset 到底是多少。
● acks=1:当 acks=1 时,leader 会在将消息写入到本地日志之后,不会等待 followers 的 ack 就直接返回成功响应。在这个配置项下,如果 leader 在返回成功响应之后,收到任意 follower 的 ack 之前(或者说任意 follower 已经复制了消息之前)就挂掉的话,这条消息就丢失了。
● acks=all:当 acks=all 时,在这个配置项下,leader 会等到所有的 ISR 都返回 ack 后,才会返回成功响应给生产者。只要至少有一个 ISR 能够存活,这条消息就不会丢失。这个配置项等同于 acks=-1。
acks 的默认值是 all。官方文档中指示该配置项重要性为低。
官方文档原文:
The number of acknowledgments the producer requires the leader to have received before considering a request complete. This controls the durability of records that are sent. The following settings are allowed:
● acks=0 If set to zero then the producer will not wait for any acknowledgment from the server at all. The record will be immediately added to the socket buffer and considered sent. No guarantee can be made that the server has received the record in this case, and the retries configuration will not take effect (as the client won't generally know of any failures). The offset given back for each record will always be set to -1.
● acks=1 This will mean the leader will write the record to its local log but will respond without awaiting full acknowledgement from all followers. In this case should the leader fail immediately after acknowledging the record but before the followers have replicated it then the record will be lost.
● acks=all This means the leader will wait for the full set of in-sync replicas to acknowledge the record. This guarantees that the record will not be lost as long as at least one in-sync replica remains alive. This is the strongest available guarantee. This is equivalent to the acks=-1 setting. Note that enabling idempotence requires this config value to be 'all'. If conflicting configurations are set and idempotence is not explicitly enabled, idempotence is disabled.
compression.type
生产者在发送消息之前,会根据compression.type选择对应的压缩算法,对发送的消息进行压缩,再进行发送。可选值以及它们的对比如下,默认是none,即不压缩。
官方文档原文:
The compression type for all data generated by the producer. The default is none (i.e. no compression). Valid values are none, gzip, snappy, lz4, or zstd. Compression is of full batches of data, so the efficacy of batching will also impact the compression ratio (more batching means better compression).
buffer.memory
生产者发送消息的缓冲区大小,单位为字节(byte)。消息在发送到kafka之前,会先被发送到缓冲区,等待被发送到kafka。如果生产者发送消息到缓冲区的速度大于消息被
官方文档原文:
The total bytes of memory the producer can use to buffer records waiting to be sent to the server. If records are sent faster than they can be delivered to the server the producer will block for max.block.ms after which it will fail with an exception. This setting should correspond roughly to the total memory the producer will use, but is not a hard bound since not all memory the producer uses is used for buffering. Some additional memory will be used for compression (if compression is enabled) as well as for maintaining in-flight requests.
Broker Configs
broker的配置,也就是Kafka服务端的配置。
Consumer Configs
消费者客户端的配置。
bootstrap.servers
参考生产者配置部分。
key.deserializer
在接收消息之前,需要将消息反序列化。这个配置项是消息的key的反序列化器,
反序列化器需要实现org.apache.kafka.common.serialization.Deserializer接口。
value.deserializer
在接收消息之前,需要将消息反序列化。这个配置项是消息的value的反序列化器,
反序列化器需要实现org.apache.kafka.common.serialization.Deserializer接口。
auto.offset.reset
该配置项决定了消费者组在读取某个partition的消息时,如果Kafka没有找到当前消费者在该partition中的offset时(也就是没有初始 offset,比如首次启动消费者组时),或者当前记录的offset无效(比如该offset的消息已被删除)时kafka的行为,支持以下选项:
earliset:将offset置为该partition的最早offsetlatest:将offset置为该partition的最新offset,也就是下一条消息的offsetby_duration:从当前时间往前推 ,重置到那个时间点的 offset。注意这个配置项在Kafka3.0以上的版本才生效。none:直接抛异常
一般来说,在生产环境下推荐使用earliest,这样可以确保不会漏消息。
注意事项:如果消费者组在宕机后超过offsets.retention.minutes配置的时间(默认7天)重启,kafka将不会保留该消费者的offset。
官方文档原文:
What to do when there is no initial offset in Kafka or if the current offset does not exist any more on the server (e.g. because that data has been deleted): ● earliest: automatically reset the offset to the earliest offset ● latest: automatically reset the offset to the latest offset ● by_duration:: automatically reset the offset to a configured from the current timestamp. must be specified in ISO8601 format (PnDTnHnMn.nS). Negative duration is not allowed. ● none: throw exception to the consumer if no previous offset is found for the consumer's group ● anything else: throw exception to the consumer. Note that altering partition numbers while setting this config to latest may cause message delivery loss since producers could start to send messages to newly added partitions (i.e. no initial offsets exist yet) before consumers reset their offsets.
enable.auto.commit
如果配置为true,该消费者组的offset会在后台周期性地提交。默认为true。
生产环境一般不推荐设置为true,因为有造成消息丢失和重复消费的风险,原因参考上文分析。
auto.commit.interval.ms
消费者组自动提交offset的频率,单位为ms。该配置项只在将enable.auto.commit配置为true时生效,默认为5s。
官方文档原文:
The frequency in milliseconds that the consumer offsets are auto-committed to Kafka if enable.auto.commit is set to true.
max.poll.records
消费者每次调用poll()函数拉取消息时,最多返回多少条消息。默认值为500。注意,消费者拉取消息是分为两个阶段的,一阶段是网络拉取,调用fetch()函数从broker拉取消息,拉取到的消息会被放在消费者本地缓存中,二阶段是调用poll()函数从缓冲区中拉取消息,到这里消息才真正被获取到。
The maximum number of records returned in a single call to poll(). Note, that max.poll.records does not impact the underlying fetching behavior. The consumer will cache the records from each fetch request and returns them incrementally from each poll.
fetch.max.bytes
kafka在单次消费者的fetch请求中返回给消费者的数据大小上限,单位为字节。默认值:52,428,800 字节(即 50 MB,在spring-kafka中默认为,100MB)。
注意,这个配置项实际上是消费者在 fetch 请求中声明的“期望值”,而最终生效的是 Broker 端fetch.max.bytes的限制(取两者最小值)。
官方文档:
The maximum amount of data the server should return for a fetch request. Records are fetched in batches by the consumer, and if the first record batch in the first non-empty partition of the fetch is larger than this value, the record batch will still be returned to ensure that the consumer can make progress. As such, this is not a absolute maximum. The maximum record batch size accepted by the broker is defined via message.max.bytes (broker config) or max.message.bytes (topic config). A fetch request consists of many partitions, and there is another setting that controls how much data is returned for each partition in a fetch request - see max.partition.fetch.bytes. Note that there is a current limitation when performing remote reads from tiered storage (KIP-405) - only one partition out of the fetch request is fetched from the remote store (KAFKA-14915). Note also that the consumer performs multiple fetches in parallel.
group.id
消费者所属的消费者组的唯一标识,也就是消费者组的名称。
A unique string that identifies the consumer group this consumer belongs to. This property is required if the consumer uses either the group management functionality by using subscribe(topic) or the Kafka-based offset management strategy.