MetaQ是一个队列模型消息中间件。经历了3个阶段:
- 在2011年1月份发布了1.0版本,从Apache Kafka衍生而来,主要用于日志传输。
- 在2012年9月份发布了2.0版本,解决了分区数受限问题,在数据库binlog同步方面得到了广泛的应用。
- 在2013年7月份发布了3.0版本,MetaQ开始广泛应用于订单处理,cache同步、流计算、IM实时消息、binlog同步等领域。
- 从3.0版本开始开源,并且改名为RocketMQ,最新版本是4.4。
综上,MetaQ借鉴了Kafka的思想,并结合互联网应用场景对性能的要求,对数据的存储结构进行了全新设计。
消费模型
metaq采用发布-订阅模型。消息的消费方式是pull方式,由消费者主动从metaq服务器拉取数据,解析成消息并消费。
消息持久性
metaq 接收到消息之后,会先把消息持久化到文件 ,并充分利用Linux文件系统内存cache来提高性能。
消息堆积能力:metaq每台服务器提供大约亿级的消息堆积能力(多个业务方共用),超过堆积阈值,订阅消息吞吐量会下降。
消息过滤
metaq支持两种过滤方式:服务器端过滤,客户端过滤。
服务器端过滤: 优点 是减少网络上无用消息的传输, 缺点 是增加服务端负担,实现复杂。
客户端过滤: 优点 是可以完全根据自己的需要定制哪些需要哪些不要, 缺点 是很多无用的消息要传输到客户端。
消息实时性
metaq客户端通过长轮询的方式连接服务端,可以保证消息非常实时,实时性不低于push
每个消息至少投递一次
Consumer先pull消息到本地,消费完之后,才会向服务器返回。
为了追求性能,metaq不保证不重复发送消息,但是正常情况下很少出现。只有网络异常、consumer启动、停止等异常情况下才会出现重复。
本质原因是网络调用的不确定性,即会出现既不成功也不失败的第三种状态。
保证消息局部有序
通过队列的特性,保证消息的顺序。
发送端,将需要保证顺序的消息发送到同一个队列中。消费端,从队列里取消息,顺序消费。
不同的几组消息,可以发送到不同的队列中,提高并行性。
metaq的存储结构
metaq的逻辑存储结构是物理队列+逻辑队列。
物理队列只有一个,采用固定大小的文件顺序存储消息。逻辑队列有多个,每个逻辑队列有多个分区,每个分区有多个索引。
a. 消息顺序写入物理文件里面,每个文件达到一定的大小,新建一个文件继续顺序写数据(消息的写入是串行的,避免了磁盘竞争)。
b. 消息的索引则顺序的写入逻辑文件中,并不存放真正的消息,只是存放指向消息的索引。
metaq对于客户端展现的是逻辑队列就是消费队列,consumer从消费队列里顺序取消息进行消费。
这种把物理和逻辑分离,消费队列更加轻量化。所以metaq可以支撑更多的消费队列数,提升消息的吞吐量,并且有一定的消息堆积能力。
缺点 :写虽然是顺序写,但是读却是随机读的
解决办法 :尽可能让读命中pageCache,减少磁盘IO次数。
metaq的所有消息都是持久化的,先写入系统PAGECACHE(页高速缓存),然后刷盘,可以保证内存与磁盘都有一份数据,访问时,直接从内存读取。
刷盘策略分为异步和同步两种。
Linux的文件Cache管理
在 Linux 操作系统中,为了加快文件的读写,当应用程序需要读取文件中的数据时,操作系统先分配一些内存,将数据从存储设备读入到这些内存中,然后再将数据分发给应用程序;当需要往文件中写数据时,操作系统先分配内存接收用户数据,然后再将数据从内存写到磁盘上。
文件 Cache 管理就是对这些由操作系统分配,并用来存储文件数据的内存的管理。
Cache 管理的优劣通过两个指标衡量:
Cache 命中率:Cache 命中时数据可以直接从内存中获取,不再需要访问低速外设,因而可以显著提高性能;
有效 Cache 的比率:有效 Cache 是指真正会被访问到的 Cache 项。
如果有效 Cache 的比率偏低,则相当部分磁盘带宽会被浪费到读取无用Cache 上,而且无用 Cache 会间接导致系统内存紧张,最后可能会严重影响性能。 在 Linux 的实现中,文件 Cache 分为两个层面,一是 Page Cache,另一个 Buffer Cache。 每一个 Page Cache 包含若干 Buffer Cache。
通过内存映射的方式读写文件
metaq在文件读写操作上做了一定的优化,使用内存映射的方式完成读写,替代了传统的IO操作,从而大大的减少了文件读写系统调用的次数,提升了IO的性能。
传统的文件访问:系统调用打开文件,获取文件描述符,使用read write 系统调用进行IO系统调用,关闭文件。这种方式是非常低效的, 每一次I/O操作都需要一次系统调用。 另外, 如果若干个进程访问同一个文件, 每个进程都要在自己的地址空间维护一个副本, 浪费了内存空间。
内存映射的方式:
- 打开文件,得到文件描述符。
- 获取文件大小
- 把文件映射成虚拟内存(mmap)
- 通过对内存的读写来实现对文件的读写(memset或memcpy)
- 卸载映射
- 关闭文件
首先建立好虚拟内存和磁盘文件之间的映射(mmap系统调用),当进程访问页面时产生一个缺页中断,内核将页面读入内存,并且更新页表指向该页面。
所有进程共享同一物理内存,物理内存中可以只存储一份数据,不同的进程只需要把自己的虚拟内存映射过去就可以了,这种方式非常方便于同一副本的共享,节省内存。经过内存映射之后,文件内的数据就可以用内存读/写指令来访问,而不是用Read和Write这样的I/O系统函数,从而提高了文件存取速度。
metaq消息底层通信组件(rocketmq?)
metaq消息的传递,通信,是使用的netty,并在netty之上作了简单的协议封装。
网络协议如下:
数据部分采用json序列化。
Push or Pull:
Push模式:很难掌握消息推送的时机和速率,因为consumer的消费速率不同。
Pull模式:consumer可以根据自己的状况选择拉取消息的时机和速率,缺点在于如果服务端没有可供消费的消息,将导致consumer不断轮询,浪费资源。
MetaQ整体结构
如图所示,MetaQ对外提供的是一个队列服务,内部实现也是完全的队列模型,这里的队列是持久化的磁盘队列,具有非常高的可靠性,并且充分利用了操作系统cache来提高性能。
是一个队列模型的消息中间件,具有高性能、高可靠、高实时、分布式特点。
Producer、Consumer、队列都可以分布式。
Producer向一些队列轮流发送消息,队列集合称为Topic,Consumer如果做广播消费,则一个consumer实例消费这个Topic对应的所有队列,如果做集群消费,则多个Consumer实例平均消费这个topic对应的队列集合。
能够保证严格的消息顺序
提供丰富的消息拉取模式
高效的订阅者水平扩展能力
实时的消息订阅机制
亿级消息堆积能力
MetaQ存储结构
MetaQ的存储结构可以支持上万的队列模型,并且可以支持消息查询、分布式事务、定时队列等功能,如下图所示。
MetaQ存储体系
MetaQ单机上万队列
MetaQ内部大部分功能都靠队列来驱动,那么必须支持足够多的队列,才能更好的满足业务需求,如图所示,MetaQ可以在单机支持上万队列,这里的队列全部为持久化磁盘方式,从而对IO性能提出了挑战。MetaQ是这样解决的Message全部写入到一个独立的队列,完全的顺序写Message在文件的位置信息写入到另外的文件,串行方式写。
通过以上方式,既做到数据可靠,又可以支持更多的队列,如图所示。
MetaQ与Notify区别
Notify侧重于交易消息,分布式事务消息方面。
MetaQ侧重于顺序消息场景,例如binlog同步。以及主动拉消息场景,例如流计算等。
网友评论