Kafka设计

Introduction

Apache Kafka是一个开源的分布式事件流平台, 用于高性能数据管道、流分析、数据集成

事件流是从事件源(如数据库、传感器、移动设备、云服务和软件应用程序)以事件流的形式实时捕获数据的实践;持久存储这些事件流以供以后检索;实时和回顾性地操作、处理和响应事件流;并根据需要将事件流路由到不同的目标技术

  • publish/subscribe(write/read)事件流
  • 存储事件流
  • 实时或追溯处理事件流

Design

目标

与传统的MQ相比,更像数据库日志

  • 支持高吞吐
  • 支持处理大量数据积压
  • 支持低延迟
  • 机器故障时支持容错

持久化

文件系统

Kafka非常依赖文件系统进行存储和缓存

使用内存缓存的坏处:

  1. OS会将内存转移到磁盘缓存
  2. Java的对象内存开销大
  3. 堆内存使用越多GC越慢
  4. 服务重启时重建缓存慢

进行的优化:

  1. 自动访问空闲内存
  2. 保存byte[]而不是Java对象
  • NO 在内存中维护尽可能多的内容,并在空间不足时将其全部刷新到文件系统
  • YES 所有数据都会立即写入文件系统上的持久日志, 不必刷新到磁盘(实际上只是被转移到内核的页面缓存中)

常数时间

Queue建立在对文件的read和append上(在日志系统中很常见), 而BTree等数据结构如果触发磁盘寻道会开销很大

优点:

  1. 操作的时间复杂度O(1)
  2. 读写不会阻塞
  3. 性能与数据量无关
  4. 支持消息保存较长时间

效率

避免效率低的磁盘访问:

  1. 过多的小型IO操作
  2. 过多的byte复制

如何避免:

  1. 将数据抽象为message set, 网络请求将消息组合在一起, 服务器将消息一次性append, 消费者一次性获取大数据块
  2. 使用标准化二进制格式, 这样数据传输时不用修改, 就可以利用OS提供的zero-copy, 在Linux中为sendfile系统调用. 使用pagecache+sendfile使磁盘几乎不会有高负载

端到端批量压缩

大部分信息冗余来源于同一种类型的不同消息, Kafka支持一批消息批量压缩

Producer

负载均衡

客户端可以控制消息的目标partition, 支持设置使用指定字段作为分区根据, 这样设计使consumer可以做局部敏感数据处理

异步发送

producer支持异步, 在内存中保留一定消息然后批处理发送以提高效率, 可以配置缓存的大小和有效时间

Consumer

consumer在每个请求中指定想要访问的偏移量

PUSH vs PULL

Kafka使用支持阻塞的PULL请求

PULL的优点

  1. consumer不会收到超出可处理上限的消息, 不同的consumer可以按照自己的处理能力获取消息
  2. PUSH需要针对可能的阻塞设置推送延迟, PULL不需要设置

简单的PULL的缺点

  1. 没有消息的时候consumer浪费CPU资源进行空轮询

Consumer记录位置

MQ需要确认消息是否被成功消费, 一些MQ引入一个中间状态”已发送未消费”

Kafka的topic的每个分区记录一个consumer位置, 不需要维护每个消息的确认状态

消息传递语义

可以在所有情况下提供多种保证()

  1. 最多一次, 消息可能会丢失, 不会重复
  2. 最少一次, 消息可能会重复, 不会丢失
  3. 精确一次

Kafka默认保证最少一次, 事务性producer/consumer可以实现精确一次

producer

Kafka支持idempotent delivery幂等生产, 重新发送消息不会导致broker中出现重复消息

consumer

Kafka需要支持一个consumer崩溃后切换到其他consumer的场景

  1. 最多一次, consumer读消息->保存位置->处理消息
  2. 至少一次, consumer读消息->处理消息->保存位置
  3. 精确一次, 使用事务更新, broker同时更新consumer的位置和处理结果

Replication

replication的单位是partition, 每个partition都有1个主节点0或多个从节点, 主写从读

replication的心跳使用ZooKeeper维护

分布式一致性

Implementation

网络层

NIO服务器

消息格式

key/value不透明


Apache Kafka - intro

Apache Kafka - doc