1.kafka名词解释
Broker:一个kafka节点就是一个broker,多个kafka节点可以组成一个kafka集群
Topic:每条kafka消息都有它的topic,kafka根据topic做消息的分类
Producer:消息的生产者,负责向broker发送消息的客户端
Consumer:消息的消费者,负责从broker拉取消息来进行消费(解析消息然后执行自己的逻辑)
Consumer Group:一个group里可以有一个或多个consumer,一种topic可以制定哪个group能消费这类消息,这条消息发出来之后,指定的group中只能有一个consumer去消费它
Partition:物理上的概念,一种topic的消息发出来之后会经过hash操作落到不同的partition中
offset:用来标记消息顺序的序号,每个partition之内会根据offset按顺序执行消息的操作
一个topic对应多个partition,partition分布在多broker上,多broker一起提供kafka服务
2.kafka的设计
持久化:从大学上操作系统课时,老师一直在强调的一个观点就是,cpu最快,内存其次,磁盘最慢,导致很多人印象中磁盘很慢很慢,再加上现在一些技术,像redis之类的是运行在内存中,在内存中做实时的数据增删改查,异步持久化到磁盘,更加印证了人们的观点。但是实际上,正确地去使用磁盘,速度将会比我们通常认为的要快的多:
从kafka官方文档中的这段话可以看出,在磁盘中的随机写确实很慢,但是如果是顺序写,其实是很快的,结合之前大学时候上的课也可以知道这是因为磁盘顺序写可以节省寻道时间,大大提升磁盘使用效率。
基于这些来考虑,kafka使用了文件来存储消息,这个文件的名字是Appender Log,同时,它没有使用我们在数据库索引中或是别的场景中使用的B/B+树之类的数据结构(时间复杂度O(logN)),而是直接使用队列,对日志文件进行读取并附加的操作,这样能使得操作的时间复杂度是O(1),而且由于是附加操作,也就是顺序写,从而省去了我上文提到的磁盘寻道的时间,大大提升时间效率。
这样设计之后,因为磁盘非常的廉价,所以如果有足够大的磁盘,我们可以近乎无限地去存储消息的记录,然后对消息存档做定期删除,kafka适合用来做大数据、日志系统这个就是原因之一。
消息整合发送:对磁盘效率上的考虑除了顺序写之外,还有多次IO的问题,kafka在这一点上的设计很好理解,一个形象的比喻就是,一次拉一车苹果去卖和一次拿一个苹果去卖,当然是前者比较合适,可以类比的像tcp分组发送,当然这种设计理念也会造成一些问题,比如正是tcp这样做,我们在使用NIO开发的时候才需要考虑半包和拆包的问题,kafka当然不用考虑半包和拆包(因为kafka就是基于tcp协议,而且从逻辑上考虑,粒度再小也不会小于一条消息的粒度,总不可能给别人发半条消息过去,这里只是和tcp分组做个类比),kafka需要考虑的是消息的即时性问题,也就是说,有可能会出现producer发了一条消息,这条消息需要等到一整个分组都发过去的时候才能发过去,但是其实这个分组发送是非常快的,而且消息队列本身就不是为了即时操作而生的,如果想要服务之间通信的即时性,还是使用RPC框架更合适。
这样设计之后,kafka不仅能够减少网络上的开销,同时还能一次性向文件中写入这一整个分组的消息,这样也是减少了磁盘上的开销。
使用sendfile优化字节复制:由于我们是将消息存储在文件当中,因此也需要考虑这个过程中是否有可以优化性能的点,传统的操作是这样的:
1、调用read函数,文件数据被copy到内核缓冲区
2、read函数返回,文件数据从内核缓冲区copy到用户缓冲区
3、write函数调用,将文件数据从用户缓冲区copy到内核与socket相关的缓冲区。
4、数据从socket缓冲