刘邦 余华平
摘要:消息代理的使用有多种原因(将处理与数据生成器分离,缓冲未处理的消息等)。Kafka作为一个分布式消息队列,可以替代更传统的消息代理,与大多数消息传递系统相比,具有更好的吞吐量,内置分区,高性能,复制和容错功能,这使其成为大规模消息处理应用程序的理想解决方案。Kafka对外使用topic的概念,生产者往topic里写消息,消费者从各个top-ic中读取消息。每个topic是由多个partition组成,虽然partition中的消息是有序的,但是多个partition是无序的,需要保证消息的有序读写,并且提高Kafka的性能。
关键词:Kafka;topic;partition;高性能;分布式消息队列
中图分类号:TP391 文献标识码:A
文章编号:1009-3044(2019132-0004-03
1概述
大数据阶段为了分析用户的行为,我们将各类日志信息收集并保存到hadoop上做离线的处理,与此同时,我们将日志信息置于检索系统中,方便高效定位问题所在。核心上,该问题是集成数据的问题,但是一个系统并不能解决所有的问题,逻辑上我们用不同的系统处理不同的业务数据,例如归类、查找、分析、缓存等。系统中产生的数据冗余没有问题,但是将不同系统中的数据进行同步时,就会产生一系列问题。Kafka处理数据冗余的做法是提供一个分布式消息队列,让数据生产者向队列的末尾添加数据,然后消费者依次从队列里面读取数据,这样保证数据以合适的形式出现在合适的地方。
2Kafka架构组件及数据流程
2.1Kafka架构组件
每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。我们在具体操作中为每类的数据信息创建一个topic,生产者(producer)即数据的发布者,该角色将消息发布到Kafka的topic中,消费者(consumer)可以从broker中读取数据,消费者可以消费多个topic中的数据。Producers和consumers进行操作时,可同时在多个topic中读写数据。Kafka集群包含一个或多个服务器,服务器节点称为broker,它的职责是负责持久化和备份具体的kafka消息。
topic:消息存放的目录即主题
Producer:生产消息到topic的一方
Consumer:订阅topic消费消息的一方
Broker:Kafka的服务实例就是一个broker
2.1.1Kafka中的topic和partition
在Kafka中,Topic是一个存储消息的逻辑概念,可以认为是一个消息集合。每条发送到Kafka集群的消息都有一个类别。我们在操作中产生的不同类型的数据,都能将其分类为不同的topic。一把情况下,一个topic会有多个消息的订阅者,当producer发布消息到某个topic时,订阅了该topic的consumer都可以接收到producer写入的新消息。Kafka为每个topic维护了分布式的分区(Partition)日志文件,所有的partition在Kafka存储的层面都是Append Log。所有发布到该parti-tion的消息都将会置于Log日志文件的尾部,在partition中按照时间顺序,每条消息均会分配一个单调递增的顺序编号,这也是我们的位移offset。系统中Offset默认是一个Long型的数字。我们可以通过该offset确定一条在该分区下的唯一消息。在分区中保证了消息的有序性,但是在topic中,信息的有序性没有得到保证。
2.2 Kafka数据流程
使用Kafka作为消息中间件,我们需要涉及包括Kafka集群,分布式協调中心(Zookeeperl,生产者,消费者在内的四个部分对象,它们协同工作,让消息高吞吐高可靠的存储和流通。生产者往topic中写数据,消费者从中读数据,每当新增一条消息时,kafka就会在对应的文件append写,用这种方式处理消息,确保kafka的性能非常高。
2.3Kafka消费模型
消息由生产者发送到Kafka集群后,会被消费者消费。一般来说我们的消费模型有两种:一种是推送模型(Push);另一种是拉取模型(pull)。
在推送模型(Push)的消息系统中,是由消息代理记录消息的消费状态。消息代理将消息推送到consumer后,然后标记该消息为已经被消费状态,但是这种方式有个缺点,它无法很好地保证消息消费的处理语义。例如,当我们已经把消息发送给consumer之后,由于网络原因或者消费进程宕机等原因,消费者没有收到该消息,如果此时我们在消费代理中将该消息标记为已消费,那将会出现该消息永久丢失的情况。如果我们采用producer收到消息后回复这种方法,消息代理需要自己记录消息的消费状态,这种方法不合适。如果我们采用推送模型,消费代理将会完全控制消息消费的速率,如果consumer发生突发情况,形成阻塞,就会出现一系列问题。基于这种情况,Kafka采取拉取模型(Poll),由自己控制消费速度及进度,consumer可以按照任意的offset进行消费。例如,消费者可以对已经消费过的消息进行重新处理,或者是消费近期的消息等。
3 Kafka高性能的实现
3.1分区
kafka是个分布式集群的系统,整个系统可以包含多个bro-ker,也就是多个服务器实例。每个主题topic会有多个分区,kafka将分区均匀地分配到整个集群中,当生产者向对应主题传递消息,消息通过负载均衡机制传递到不同的分区以减轻单个服务器实例的压力。一个Consumer Group中可以有多个consumer,多个consumer可以同时消费不同分区的消息,大大地提高了消费者的并行消费能力。但是一个分区中的消息只能被一个Consumer Group中的一个consumer消费。如图3所示。
3.2网络传输上减少开销
3.2.1批量发送
在发送消息的时候,kafka不会直接将少量数据发送出去,否则每次发送少量的数据会增加网络传输频率,降低网络传输效率。kafka会先将消息缓存在内存中,当超过一个的大小或者超过一定的时间,那么会将这些消息进行批量发送。
3.2.2端到端压缩
网络传输时数据量小时也可以减小网络负载,kafaka会将这些批量的数据进行压缩,将一批消息打包后进行压缩,发送broker服务器后,最终这些数据还是提供给消费者用,所以数据在服务器上还是保持压缩状态,不会进行解压,而且频繁的压缩和解压也会降低性能,最终还是以压缩的方式传递到消费者的手上。
3.3顺序读写
kafka是个可持久化的日志服务,它将数据以数据日志的形式进行追加,最后持久化在磁盘中。katka消息存储时依赖于文件系统,我们普遍认为磁盘的性能比不上内存性能,但是kafka却将磁盘性能发挥得淋漓尽致。在一个由6个7200rpm的SATA硬盘组成的RAID-5磁盘阵列上,线性写入(linearwrite)的速度大约是300MB/秒,但随即写入却只有50k/秒。可见磁盘的线性和随机读写的速度差距甚大。为了利用数据的局部相关性,操作系统从磁盘中读取数据以数据块为单位,将一个数据块读入内存中,如果有相邻的数据,就不用再去磁盘中读取。在某些情况下,顺序磁盘访问能比随机内存访问还要快。同时在写数据的时候也是将一整块数据块写人磁盘中,大大提升了10效率。
现代操作系统乐于将更多的空闲内存来当作磁盘缓存。当我们在程序中对数据进行缓存时,可能这些数据已经缓存在了操作系统的缓存页中。我们将缓存的操作逻辑交给操作系统,那么比我们自己维护来得更加高效。所以使用磁盘的方式进行线性地读取数据也有很高的效率。
kafka将消息追加到日志文件中,正是利用了磁盘的顺序读写,来提高读写效率。我们平时操作磁盘可能会用Btree这种数据结构,但是运算的时间复杂度为0(10gN),持久化队列利用追加日志的方式构建,生产者将消息追加到日志尾部,消费者读取头部的消息,两者互不干扰,也不需要加锁,提高了性能,同时时间复杂度为O(1)。
3.4零拷贝
kafka将数据以日志的形式保存在磁盘中。当消费者向服务器请求数据,那么需要从文件传输到socket中。那么从文件到sueker需要以下这些步骤:
①调用read陷入内核模式,操作系统将数据从磁盘读到内核缓冲区;
②然后从内核态切换到用户态,应用程序将数据从内核空间读取到用户空间的缓冲区;
③然后应用程序将数据写带内核空间的socket缓冲区;
④最后操作系统将socket缓冲区的数据拷贝到网卡接口缓冲区并发出去。
从上面可见,当我们将数据从文件传输到socket最后发送出去经过了好几次拷贝,同时还有好几次的用户态和内核态的切换,我们知道用户态和内核态的切换也是很耗时的,那么多次拷贝对性能的影响更是雪上加霜。
从上面的过程来看,可以看出没必要从内核空间的缓冲区拷贝到用户空间。所以零拷贝技术正是改进了这项缺点,零拷贝将文件内容从磁盘通过DMA引擎复制到内核缓冲区,而且没有把数据复制到socket缓冲区,只是将数据位置和长度信息的描述符復制到了socket缓存区,然后直接将数据传输到网络接口,最后发送。这样大大减小了拷贝的次数,提高了效率,kafka正是调用linux系统给出的sendfile系统调用来使用零拷贝。
3.5优秀的文件存储机制
之前说过一个主题可以有多个分区,假设只有一个服务器broker,那么多个分区必然是存在一个服务器上。kafka将一个分区以一个目录的方式存储,目录的命名为topicname-分区下标,例如有个topic叫作hello,有3个分区,那么就有三个文件夹分别为hello-0,hello-1,hello-2。在一个分区文件夹中,又分为多个段文件。段文件又由一个index索引文件和一个log实质的数据日志文件构成。文件的命名规则为日志文件中第一个消息的offset值一1,offset可以理解为消息id,例如一个000…0015354.10g这个文件中消息最小的offset为15353。
log数据文件由消息和偏移量构成,而索引文件中的索引用的是稀疏索引。稀疏索引减少的索引文件的大小,索引文件中存着消息的物理偏移量。
4总结
Kafka中生产者及消费者是直接与broker进行交互实现生产消费功能,Kalka在设计上并未采用传统系统中通过增加一层代理实现系统的平行扩展能力。Kafka在设计中通过内部路由协议,实现了生产者与消费者可以直接与broker进行路由协商,从而实现了客户端直接与broker进行生产消费,而不需要借助第三方代理。无代理的方式不仅会减少整个数据链路的长度,降低延迟,也可以提高整个系统的稳定性,而且也会节省大量的成本。
在优化其性能方面,采用分区、网络传输减少开销、顺序读写、零拷贝、优秀的文件存储机制等方式,提高Kafka性能,另外我们也可以通过进一步了解Kafka的架构,找出它可能的瓶颈点,然后在针对瓶颈点进行优化优化,根据实际的需求,我们可以做出对应的优化,提高其性能。