Skip to main content

Kafka

Kafka

Apache Kafka

分布式消息事件流:

  • Kafka作为一个集群,运行在一台或者多台服务器上.
  • Kafka 通过 topic 对存储的流数据进行分类。
  • 每条记录中包含一个key,一个value和一个timestamp(时间戳)

pub(write) / sub(read)

基于日志的存储, append-only + seq-write + write-behind 速度快

kafka的优化

相比于维护 in-memory cache 或者其他结构,使用文件系统和 pagecache 显得更有优势--我们可以通过自动访问所有空闲内存将可用缓存的容量至少翻倍,并且通过存储紧凑的字节结构而不是独立的对象,有望将缓存容量再翻一番。 这样使得32GB的机器缓存容量可以达到28-30GB,并且不会产生额外的 GC 负担。此外,即使服务重新启动,缓存依旧可用,而 in-process cache 则需要在内存中重建(重建一个10GB的缓存可能需要10分钟),否则进程就要从 cold cache 的状态开始(这意味着进程最初的性能表现十分糟糕)。 这同时也极大的简化了代码,因为所有保持 cache 和文件系统之间一致性的逻辑现在都被放到了 OS 中,这样做比一次性的进程内缓存更准确、更高效。如果你的磁盘使用更倾向于顺序读取,那么 read-ahead 可以有效的使用每次从磁盘中读取到的有用数据预先填充 cache。

这里给出了一个非常简单的设计:相比于维护尽可能多的 in-memory cache,并且在空间不足的时候匆忙将数据 flush 到文件系统,我们把这个过程倒过来。**所有数据一开始就被写入到文件系统的持久化日志中,**而不用在 cache 空间不足的时候 flush 到磁盘。实际上,这表明数据被转移到了内核的 pagecache 中。

  • 大量的小型 I/O 操作的优化:批处理,Consumer 每次获取多个大型有序的消息块,并由服务端 依次将消息块一次加载到它的日志中
  • 字节拷贝的优化:共享的标准化的二进制消息格式,从pagecache直接sendfile发送到socket+zero copy

每个用户(Consumer)维护一个队列的阅读偏移量offset -> 要求topic存储的是相同对象(非异构)

对于每一个topic, Kafka集群都会维持一个分区日志

每个分区都是有序且顺序不可变的记录集,并且不断地追加到结构化的commit log文件。分区中的每一个记录都会分配一个id号来表示顺序,我们称之为offset,offset用来唯一的标识分区中每一条记录。

Kafka 集群保留所有发布的记录—无论他们是否已被消费

事实上,在每一个消费者中唯一保存的元数据是offset(偏移量)即消费在log中的位置.偏移量由消费者所控制:通常在读取记录后,消费者会以线性的方式增加偏移量,但是实际上,由于这个位置由消费者控制,所以消费者可以采用任何顺序来消费记录。例如,一个消费者可以重置到一个旧的偏移量,从而重新处理过去的数据;也可以跳过最近的记录,从"现在"开始消费。

如果不同类型的对象,则需要其他信息

大的topic: 分区, 按照时间,用户姓名hash分等等

分区的用处:

第一,当日志大小超过了单台服务器的限制,允许日志进行扩展。每个单独的分区都必须受限于主机的文件限制,不过一个主题可能有多个分区,因此可以处理无限量的数据。(分区作为创建replica的单位)

第二,可以作为并行的单元集

分区的带来的水平拓展能力很强,不同分区可以跑在不同机器上,支持主从备份等,由kafka cluster维护

每个分区都有一台 server 作为 “leader”,零台或者多台server作为 follwers 。leader server 处理一切对 partition (分区)的读写请求,而follwers只需被动的同步leader上的数据。当leader宕机了,followers 中的一台服务器会自动成为新的 leader。每台 server 都会成为某些分区的 leader 和某些分区的 follower,因此集群的负载是平衡的。

Customer Groups: 同组多个消费者同时处理,可以互相心跳动态接管挂掉的节点的消息等(sub的基础单元是一个消费者组而不是一个消费者)

Kafka 只保证分区内的记录是有序的,而不保证主题中不同分区的顺序。

推还是拉:

Kafka 在这方面采取了一种较为传统的设计方式,也是大多数的消息系统所共享的方式:即 producer 把数据 push 到 broker,然后 consumer 从 broker 中 pull 数据

优点:

  • 下游容易做降级(高负载时backoff)
  • 上游容易批处理

一致性保证

消费者的位置

令人惊讶的是,持续追踪已经被消费的内容是消息系统的关键性能点之一。

大多数消息系统都在 broker 上保存被消费消息的元数据。也就是说,当消息被传递给 consumer,broker 要么立即在本地记录该事件,要么等待 consumer 的确认后再记录。这是一种相当直接的选择,而且事实上对于单机服务器来说,也没与其它地方能够存储这些状态信息。 由于大多数消息系统用于存储的数据结构规模都很小,所以这也是一个很实用的选择——因为只要 broker 知道哪些消息被消费了,就可以在本地立即进行删除,一直保持较小的数据量。

也许不太明显,但要让 broker 和 consumer 就被消费的数据保持一致性也不是一个小问题。如果 broker 在每条消息被发送到网络的时候,立即将其标记为 consumed,那么一旦 consumer 无法处理该消息(可能由 consumer 崩溃或者请求超时或者其他原因导致),该消息就会丢失。 为了解决消息丢失的问题,许多消息系统增加了确认机制:即当消息被发送出去的时候,消息仅被标记为sent 而不是 consumed;然后 broker 会等待一个来自 consumer 的特定确认,再将消息标记为consumed。这个策略修复了消息丢失的问题,但也产生了新问题。 首先,如果 consumer 处理了消息但在发送确认之前出错了,那么该消息就会被消费两次。第二个是关于性能的,现在 broker 必须为每条消息保存多个状态(首先对其加锁,确保该消息只被发送一次,然后将其永久的标记为 consumed,以便将其移除)。 还有更棘手的问题要处理,比如如何处理已经发送但一直得不到确认的消息。

Kafka 使用完全不同的方式解决消息丢失问题。Kafka的 topic 被分割成了一组完全有序的 partition,其中每一个 partition 在任意给定的时间内只能被每个订阅了这个 topic 的 consumer 组中的一个 consumer 消费。这意味着 partition 中 每一个 consumer 的位置仅仅是一个数字,即下一条要消费的消息的offset。这使得被消费的消息的状态信息相当少,每个 partition 只需要一个数字。这个状态信息还可以作为周期性的 checkpoint。这以非常低的代价实现了和消息确认机制等同的效果。

这种方式还有一个附加的好处。consumer 可以回退到之前的 offset 来再次消费之前的数据,这个操作违反了队列的基本原则,但事实证明对大多数 consumer 来说这是一个必不可少的特性。 例如,如果 consumer 的代码有 bug,并且在 bug 被发现前已经有一部分数据被消费了, 那么 consumer 可以在 bug 修复后通过回退到之前的 offset 来再次消费这些数据。

消息语义保证:

  • At most once——消息可能会丢失但绝不重传。
  • At least once——消息可以重传但绝不丢失。
  • Exactly once——这正是人们想要的, 每一条消息只被传递一次.

kafka默认是at least once, 可以配置实现at most once, 通过处理offset可以和上层应用协作达到exactly once

kafka(mq类型的异步)的缺点

2. 消息重复消费

  • 缺点:在某些情况下,如消费者重启或重新平衡时,可能会出现消息重复消费的问题。
  • 克服方法
    • 使用消费者组:确保每个分区的消息只被一个消费者实例消费。
    • 手动提交偏移量:关闭自动提交,确保消息处理成功后再手动提交偏移量。
    • 幂等处理逻辑:设计消息处理逻辑时,尽量使其成为幂等操作,即相同的消息即使被处理多次也不会产生副作用。

3. 消息丢失和顺序问题

  • 缺点:在高并发和网络不稳定的情况下,可能会出现消息丢失或顺序混乱的问题。
  • 克服方法
    • 使用事务性生产者和消费者:Kafka支持事务性消息,可以确保消息处理的原子性和一致性。
    • 消息重试机制:在消费者端实现消息重试机制,确保消息不会因为临时错误而丢失。

4. 资源消耗较大

  • 缺点:Kafka需要一定的资源来运行,包括磁盘空间、内存和CPU。
  • 克服方法
    • 资源优化:合理配置Kafka的资源,根据实际需求调整磁盘空间、内存和CPU的分配。
    • 监控和调优:使用监控工具(如Prometheus、Grafana)监控Kafka的性能,及时发现并解决资源瓶颈问题。

5. 配置和管理复杂

  • 缺点:Kafka的配置和管理相对复杂,需要进行详细的配置和维护。
  • 克服方法
    • 自动化工具:使用自动化配置管理工具(如Ansible、Puppet)来简化配置和部署过程。
    • 监控和告警:使用监控工具(如Zabbix、Prometheus)设置告警,及时发现并处理问题。

6. 数据一致性问题

  • 缺点:在分布式系统中,数据一致性是一个挑战,Kafka的消息传递机制可能会导致数据不一致。
  • 克服方法
    • 事务支持:使用Kafka的事务机制,确保消息的原子性和一致性。
    • 外部存储:在必要时,使用外部存储(如数据库)来管理偏移量,确保数据的一致性