Kafka分布式消息队列

分布式消息队列

  1. 可快速持久化。通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能
  2. 高吞吐量。即使是非常普通的硬件Kafka也可以支持每秒数百万的消息
  3. 完全的分布式系统。它的Broker、Producer、Consumer都原生地支持分布式,自动支持负载均衡

基本概念

  • Broker 一个实例或节点
  • Topic
    • Topic是Kafka中同一类数据的集合,相当于数据库中的表
  • Partition
    • 分区是一个有序的、不可修改的消息队列,分区内消息有序存储
    • 一个Topic可分为多个Partition,相当于把一个数据集分成多份,分别存储不同的分区中
    • Parition是物理概念,每个分区对应一个文件夹,其中存储分区的数据和索引文件
    • 主要作用是提高并行吞吐率
    • partiton命名规则为topic名称+有序序号
    • 建议partition的数量也需要大于集群中Broker的数量,这样可以让Partition Leader尽量均匀地分布在各个Broker中
  • Replication
    • 每个partition都有多个replicate
    • 每个partition都有一个leader(主副本),零个或多个follower(从副本)
    • 所有的读写都只由leader来完成,follower只从leader同步消息,并不对外服务

Kafka将元数据保存在Zookeeper中,负责Kafka集群管理,包括配置管理、动态扩展、Broker负载均衡、Leader选举,以及 Consumer Group变化时的Rebalance等

工作机制

  • Kafka发送端采用push模式将消息发送到broker
  • Kafka消费端采用pull模式订阅并消费消息
  • Partition是一个FIFO队列, 写入消息采用在队列尾部追加的方式,消费消息采用顺序读取的方式
  1. producer往broker发布一条消息,broker仅仅持久化消息
  2. consumer主动发起pull,告诉broker一个offset,broker把offset对应的消息返回给consumer

kafka可以设置多久删除和多大删除

数据存储

实现上一个partition对应一个文件夹,一个segment对应两个文件:一个数据文件,一个索引文件

  • Segment(段文件)
    • 最小数据存储单元
    • 一个Partition包含多个Segment文件,每个Segment大小相等,这种特性方便old segment file快速被删除。
    • *.index 索引文件
      • 索引内容格式:offset,position
      • 采用稀疏存储方式
      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-prBaHO1b-1613137854776)(assets/c415ed42.png)]
    • *.log 数据文件
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8YAjQKgK-1613137854778)(assets/69e4b0a6.png)]
    • segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。
  • offset
    • partition中的每个消息都有一个连续的序列号叫做offset,用于partition唯一标识一条消息,也可以根据Offset快速定位一个数据文件
    • 这个offset是partition内部的,而不是全局的

问题

如何保证消息可靠性?

ack + offset

即保证不重复,保证不丢失

最理想的情况是消息发送成功,并且只发送了一次,这种情况叫做exactly-once,但是不可避免的会发生消息发送失败以及消息重复发送的情况

为了解决这类问题,在producer端,当一个消息被发送后,producer会等待broker发送响应,收到响应后producer会确认消息已经被正确发送给kafka,否则就会重新发送

在consumer端,因为broker记录了partition中的offset值,这个值指向consumer下一个消费的消息,如果consumer收到消息但是消费失败,broker可以根据offset值来找到上一个消息,同时consumer还可以控制offset值,来对消息进行任意处理

为什么具有如此高性能

https://mp.weixin.qq.com/s?__biz=MzIxMjAzMDA1MQ==&mid=2648945468&idx=1&sn=b622788361b384e152080b60e5ea69a7#%23

  • 磁盘顺序读写效率高于内存随机读写,消息持久化能力即写操作,写操作是直接追加到一个segment文件的末尾,可以做到以时间复杂度为O(1)的方式提供消息持久化能力
    • https://queue.acm.org/detail.cfm?id=1563874
    • Apache Kafka – A High Throughput Distributed Messaging System的观点
  • 无需在内存里维护大量的数据,Kafka不需要担心GC的问题
  • 另外Kafka直接通过sendfile系统调用避免了内核态和用户态之间切换以及不必要的数据复制。

1.顺序写入

因为硬盘是机械结构,每次读写都会寻址->写入,其中寻址是一个“机械动作”,它是最耗时的。所以硬盘最“讨厌”随机I/O,最喜欢顺序I/O。为了提高读写硬盘的速度,Kafka就是使用顺序I/O。如果一个topic建立多个分区那么每个parathion都是一个文文件,收到消息后Kafka会把数据插入到文件末尾。

2. Memory Mapped Files(内存映射文件)

64位操作系统中一般可以表示20G的数据文件,它的工作原理是直接利用操作系统的Page来实现文件到物理内存的直接映射。完成映射之后你对物理内存的操作会被同步到硬盘上。

3. Kafka高效文件存储设计特点

Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
通过索引信息可以快速定位message和确定response的最大大小。通过index元数据全部映射到memory(内存映射文件),可以避免segment file的IO磁盘操作。通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。

kafka中的topic为什么要进行分区?

为了性能考虑,如果topic内的消息只存于一个broker,那这个broker会成为瓶颈,无法做到水平扩展。所以把topic内的数据分布到整个集群就是一个自然而然的设计方式。Partition的引入就是解决水平扩展问题的一个方案。这样,producer可以将数据发送给多个broker上的多个partition,consumer也可以并行从多个broker上的不同paritition上读数据,实现了水平扩展

如果一个Topic对应一个文件,那这个文件所在的机器I/O将会成为这个Topic的性能瓶颈,而有了Partition后,不同的消息可以并行写入不同broker的不同Partition里,极大的提高了吞吐率。

在partition中如何通过offset查找message

例如读取offset=368776的message,需要通过下面2个步骤查找。

  • 第一步查找segment file 上述图2为例,其中00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0.第二个文件00000000000000368769.index的消息量起始偏移量为368770 = 368769 + 1.同样,第三个文件00000000000000737337.index的起始偏移量为737338=737337 + 1,其他后续文件依次类推,以起始偏移量命名并排序这些文件,只要根据offset 二分查找文件列表,就可以快速定位到具体文件。 当offset=368776时定位到00000000000000368769.index|log
  • 第二步通过segment file查找message 通过第一步定位到segment file,当offset=368776时,依次定位到00000000000000368769.index的元数据物理位置和00000000000000368769.log的物理偏移地址,然后再通过00000000000000368769.log顺序查找直到offset=368776为止。

为什么要引入segment?

如果不引入segment,一个partition直接对应一个文件(应该说两个文件,一个数据文件,一个索引文件),那这个文件会一直增大。同时,在做data purge时,需要把文件的前面部分给删除,不符合kafka对文件的顺序写优化设计方案。引入segment后,每次做data purge,只需要把旧的segment整个文件删除即可,保证了每个segment的顺序写

其他

对于传统的message queue而言,一般会删除已经被消费的消息,而Kafka集群会保留所有的消息,无论其被消费与否。当然,因为磁盘限制,不可能永久保留所有数据(实际上也没必要),因此Kafka提供两种策略删除旧数据。一是基于时间,二是基于Partition文件大小。

Kafka会为每一个Consumer Group保留一些metadata信息——当前消费的消息的position,也即offset。这个offset由Consumer控制。正常情况下Consumer会在消费完一条消息后递增该offset。当然,Consumer也可将offset设成一个较小的值,重新消费一些消息。因为offet由Consumer控制,所以Kafka broker是无状态的,它不需要标记哪些消息被哪些消费过,也不需要通过broker去保证同一个Consumer Group只有一个Consumer能消费某一条消息,因此也就不需要锁机制,这也为Kafka的高吞吐率提供了有力保障。

参考:http://www.jasongj.com/2015/03/10/KafkaColumn1/

https://blog.csdn.net/thewayma/article/details/4287170

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值