本文记录笔者在学习 Kafka 过程中,对 Kafka 概念理解和问题整理。
假设我们需要一个任务队列,用于处理异步任务,它大概长这样。
典型的先进先出(FIFO)队列,一个生产者,一个消费者,数据消费完即移除队列。
理想情况下,生产和消费能力相当,这样一个队列就满足需求了。但是现实往往不那么理想。
这样的设计有几个问题:
- 消费能力跟不上生产,队列会不会变得很庞大。
- 第一次消费失败,没有尝试机制。
- 单消费者。
- 数据持久性问题。
实际业务处理时,以上问题都需要解决,这里还没有考虑到分布式,数据副本等情形。
基于以上的思考,我们看下 Kafka 怎么解决了这些问题。
Kafka 架构图:
如图所示,Kafka 支持多生产者,多Topic(主题,相当于一个独立队列),多消费者。
一些基本概念:
- Producer 即生产者
- Topic 主题,可以理解为消息队列
- Partition 为 Topic 的分区,Kafka 消息的存储是按 Partion 为单位的。一个 Topic 可以指定一个或多个分区,当指定一个时就变成我们最上面设想的队列。
- Broker 代理,消费者所消费的实例。
Kafka 数据时序性问题
Kafka Topic 数据存储时会(平均)存储在 Partition上。如图所示:
在实际应用中,我们需要消费 Topic 时能保定时序性。而 Kafka 只能保证在分区内时序性。
至于 Topic 层面,则是不保证的。在严格要求时序的场景下,怎么办呢?
有些同学肯定想到了,一个 Topic 只设置一个 Partition 不就可以了?答案是确实可以。但是这样牺牲了可扩展性,消费者也只能一个,性能也大大折扣。
哪还有其他方法么?自然是有的。 Kafka 允许生产消息的时候指定 Key ,Kafka 会将 Key hash,同一个 Key 分配在同一个分区中。比如订单号,只要保证一个订单号都分配在同一个分区,就可以保证数据时序性。
消息重复和消费幂等
消息队列Kafka版消费的语义是at least once, 也就是至少投递一次,保证消息不丢失,但是无法保证消息不重复。在出现网络问题、客户端重启时均有可能造成少量重复消息,此时应用消费端如果对消息重复比较敏感(例如订单交易类),则应该做消息幂等。
以数据库类应用为例,常用做法是:
- 发送消息时,传入key作为唯一流水号ID。
- 消费消息时,判断key是否已经消费过,如果已经消费过了,则忽略,如果没消费过,则消费一次。 当然,如果应用本身对少量消息重复不敏感,则不需要做此类幂等检查。
先整理这么多,To Be Continued…
参考: