大部分消息系统在 broker 端的维护消息被消费的记录:一个消息被分发到
consumer 后 broker 就马上进行标记或者等待 customer 的通知后进行标记。这
样也可以在消息在消费后立马就删除以减少空间占用。
但是这样会不会有什么问题呢?如果一条消息发送出去之后就立即被标记为消费
过的,一旦 consumer 处理消息时失败了(比如程序崩溃)消息就丢失了。为了
解决这个问题,很多消息系统提供了另外一个个功能:当消息被发送出去之后仅
仅被标记为已发送状态,当接到 consumer 已经消费成功的通知后才标记为已被
消费的状态。这虽然解决了消息丢失的问题,但产生了新问题,首先如果 consumer
处理消息成功了但是向 broker 发送响应时失败了,这条消息将被消费两次。第二
个问题时,broker 必须维护每条消息的状态,并且每次都要先锁住消息然后更改
状态然后释放锁。这样麻烦又来了,且不说要维护大量的状态数据,比如如果消
息发送出去但没有收到消费成功的通知,这条消息将一直处于被锁定的状态,
Kafka 采用了不同的策略。Topic 被分成了若干分区,每个分区在同一时间只被一
个 consumer 消费。这意味着每个分区被消费的消息在日志中的位置仅仅是一个
简单的整数:offset。这样就很容易标记每个分区消费状态就很容易了,仅仅需要
一个整数而已。这样消费状态的跟踪就很简单了。
页这带来了另外一个好处:consumer 可以把 offset 调成一个较老的值,去重新消
费老的消息。这对传统的消息系统来说看起来有些不可思议,但确实是非常有用
的,谁规定了一条消息只能被消费一次呢?