kafka官方文档翻译

4.2 持久化
不要害怕文件系统

磁盘读写的快慢和如何使用有关,合理设计可以使磁盘和网络一样快。
配置为six 7200rpm SATA RAID-5 array的JBOD,顺序写的性能为600MB/sec,随机写的性能为100k/sec,相差6000倍。顺序读写是磁盘最希望被使用的方式,而且操作系统做了很多优化。
现代操作系统提供预读,后写的功能,(内存中的buff/cache)。应用程序不用再实现一遍。而且关于java内存,我们知道:

  1. 一个对象所需要的内存经常是实际存储数据大小的两倍甚至更多。
  2. 随着堆中数据增加,垃圾回收变得越来越慢。

使用操作系统的缓存,可以降低JVM的内存占用,重启应用时,也不用重新加载数据。

稳定的时间复杂度
4.3 性能
端到端的批量压缩

有些场景下瓶颈不是CPU或者磁盘,而是网络。用户可以每次发送数据前自己压缩数据,但是这样会有很低的压缩比。很多场景下的数据冗余都是因为有很多相同类型的数据,高效的压缩是将很多同类数据一起压缩而不是单条压缩。
producer将一个批次的数据压缩,发送到broker,broker存储压缩过的数据,consumer端接收到数据后解压。
支持GZIP, Snappy, LZ4 and ZStandard compression protocols
consumer端可以同时消费压缩和未压缩的数据。所以header中有标识是否压缩。
ByteBufferMessageSet格式如下。

1 byte magic1 byte compression-attributes4 byte CRC32 of the payload
4.4 The Producer
负载均衡

producer直接发送数据到对应leader partition所在的broker,不需要任何中转。所以每一个kafka节点都需要知道哪个节点是活着的,对应leader partition在哪个节点上,并且能够响应相应的请求。

异步发送

批量操作是提交效率的高效方法之一,kafka在内存中缓存数据,每次请求发送一批数据。支持两个参数,缓存数据不能超过 X 字节,或者不能超过 n 毫秒。

4.5 The Consumer

consumer从leader partition所在的broker拉取数据。每次请求传一个offset,broker返回从该offset开始往后的一批数据。consumer可以控制offset来重复消费。

Consumer 偏移量
离线数据加载
静态组

每当consumer个数发生改变(新的consumer加入或者旧的consumer掉线),会发生rebalance, rebalance期间所有consumer无法消费数据。rebalance严重影响消费组吞吐,特别是group比较大,consumer个数比较多的时候,rebalance需要很长时间。为了解决这个问题,新版的group管理协议允许consumer提供一个持久的entity ids。基于这些id,之前分配的partition不变,不重新rebalance。
如果要使用这个特性,broker和client都要升级到2.3版本

4.6 消息发送语义

At most once
At least once
Exactly once

4.7 复制

5 实现

5.1 网络层

网络层是一个简单直接的NIO server。

5.2 消息

消息包含一个变长的header,一个变长的key的字节数组,和一个变长的value的字节数组。根据场景选用不同的序列化方式。RecordBatch类提供简单批量读写message的方法

5.3 消息格式

一个批次(Record Batch)包含很多条消息(Record), 批次(Record Batch)和每一条消息(Record)都有自己的header。

5.3.1 Record Batch 格式

下面的格式就是存在磁盘上的格式

baseOffset: int64
batchLength: int32
partitionLeaderEpoch: int32
magic: int8 (current magic value is 2)
crc: int32
attributes: int16
    bit 0~2:
        0: no compression
        1: gzip
        2: snappy
        3: lz4
        4: zstd
    bit 3: timestampType
    bit 4: isTransactional (0 means not transactional)
    bit 5: isControlBatch (0 means not a control batch)
    bit 6~15: unused
lastOffsetDelta: int32
firstTimestamp: int64
maxTimestamp: int64
producerId: int64
producerEpoch: int16
baseSequence: int32
records: [Record]
5.3.2 Record格式

磁盘上的存储格式如下

length: varint
attributes: int8
    bit 0~7: unused
timestampDelta: varint
offsetDelta: varint
keyLength: varint
key: byte[]
valueLen: varint
value: byte[]
Headers => [Header]
5.3.2.1 Record Header 格式
headerKeyLength: varint
headerKey: String
headerValueLength: varint
Value: byte[]
5.4 Log

文件存储如下,每个partition建一个文件夹,以topic-partition命名。数据文件以该文件存储的起始offset命名,数据文件是一个"log entrie"的队列,每个"log entrie"包含一个4字节的整数表示message大小N,和N个字节的message。

drwxr-xr-x 2 root root  4096 Aug 10 15:14 access-0
drwxr-xr-x 2 root root  4096 Aug 10 14:50 access-1
drwxr-xr-x 2 root root  4096 Aug 10 15:14 access-6
-rw-r--r-- 1 root root     546784 Aug 10 08:13 00000000024134685350.index
-rw-r--r-- 1 root root 1073737636 Aug 10 08:13 00000000024134685350.log
-rw-r--r-- 1 root root     818724 Aug 10 08:13 00000000024134685350.timeindex
-rw-r--r-- 1 root root     372720 Aug 10 12:12 00000000024161786878.index
-rw-r--r-- 1 root root 1073695705 Aug 10 12:12 00000000024161786878.log
-rw-r--r-- 1 root root     555840 Aug 10 12:12 00000000024161786878.timeindex
-rw-r--r-- 1 root root         20 Aug 10 10:43 leader-epoch-checkpoint

用offset当做消息的id是一个亮点。开始的想法是用producer生成的GUID, 然后每个broker维护一个GUID到offset的映射。但是既然每个consumer都要持有一个serverId,那全局唯一的GUID就不能发挥它的价值。另外,维持一个随机ID到offset的映射需要一个大量随机读写磁盘的索引结构。所以为了简化查找,我们决定用brokerId,partitionId和一个递增的counter去唯一确定一条消息。用offset去标识消息就变得自然而然了。
在这里插入图片描述

写磁盘

数据会顺序的写到文件末尾,数据文件会滚动切换到下一个文件(根据配置决定一个文件的大小)。两个配置控制操作系统强制刷新数据到磁盘上,消息的条数和缓存时间。

读数据

每次读会提供一个offset和读取的最大字节数S。如果单条消息大于S,则会重试,每次重试S扩大两倍直到该条消息读取成功。broker端可以设置接收的单条消息的最大字节,超过该大小的丢弃。
consumer端试图消费一个不存在的offset时,会收到OutOfRangeException,可以根据情况重置消费或者失败。
返回给consumer的数据格式

MessageSetSend (fetch result)
 
total length     : 4 bytes
error code       : 2 bytes
message 1        : x bytes
...
message n        : x bytes
MultiMessageSetSend (multiFetch result)
 
total length       : 4 bytes
error code         : 2 bytes
messageSetSend 1
...
messageSetSend n
删除数据

两个参数控制过期策略:时间,大小
为了避免在删除数据的时候禁止读,删除的时候使用copy-on-write

5.5 其他
Consumer追踪offset

kafka允许group里的consumer存储offset到指定的broker(group coordinator)。consumerGroup 根据groupName被分配到对应的coordinator。
当group coordinator收到一个OffsetCommitRequest,它会把数据写入一个特殊的topic(__consumer_offsets),当所有的副本都写入消息后,才会返回response给consumer。coordinator会在内存中维护提交的offset。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值