请自行补充下相关基础知识
是一个基于“发布-订阅”的队列模型消息中间件,服务端使用JAVA编写,客户端支持JAVA、C++。对外已开源,名字叫RocketMQ。
metaq采用发布-订阅模型,发布者发布消息到metaq,订阅者向metaq订阅消息。消息的消费方式是pull方式,由消费者主动从metaq服务器拉取数据,解析成消息并消费。
metaq 接收到消息之后,会先把消息持久化到本地。常用的持久化方式:
metaq使用的是 持久化到文件 ,并充分利用Linux文件系统内存cache来提高性能。
注: 持久化部分的性能会直接影响消息中间件的性能。
消息堆积能力:metaq每台服务器提供大约亿级的消息堆积能力(多个业务方共用),超过堆积阈值,订阅消息吞吐量会下降。
对于应用比较多,访问量比较大的情况,消息量也就随之增大,一方面服务端给每个客户端发送消息时,总不能把全站的消息都发送过去,这样大量的无用消息在网络上传输是一种资源浪费。另一方面,客户端也不需要接收所有的消息,而只需要接收自己需要的消息。这时,消息中间件就需要一个消息过滤的功能。
metaq支持两种过滤方式:服务器端过滤,客户端过滤。
metaq客户端通过长轮询的方式连接服务端,可以保证消息非常实时,实时性不低于push
Consumer先pull消息到本地,消费完之后,才会向服务器返回。
为了追求性能,metaq并不保证消息不重复发送,但是正常情况下很少出现。只有网络异常,consumer启动、停止等异常情况下才会出现重复。
本质原因是网络调用的不确定性,即会出现既不成功也不失败的第三种状态。
通过队列的特性,保证消息的顺序。
发送端,将需要保证顺序的消息发送到同一个队列中。消费端,从队列里取消息,顺序消费。
不同的几组消息,可以发送到不同的队列中,提高并行性。
metaq的逻辑存储结构是一种物理队列+逻辑队列的结构。
物理队列只有一个,采用固定大小的文件顺序存储消息。逻辑队列有多个,每个逻辑队列有多个分区,每个分区有多个索引。
a.消息顺序写入物理文件里面,每个文件达到一定的大小,新建一个文件继续顺序写数据(消息的写入是串行的,避免了磁盘竞争)。
b.消息的索引则顺序的写入逻辑文件中,并不存放真正的消息,只是存放指向消息的索引。
metaq对于客户端展现的是逻辑队列就是消费队列,consumer从消费队列里顺序取消息进行消费。
这种设计是把物理和逻辑分离,消费队列更加轻量化。所以metaq可以支撑更多的消费队列数,提升消息的吞吐量,并且有一定的消息堆积能力。
缺点 :
写虽然是顺序写,但是读却是随机读的
解决办法 :尽可能让读命中pageCache,减少磁盘IO次数 (参考下文所述:Linux的文件Cache管理)
metaq的所有消息都是持久化的,先写入系统PAGECACHE(页高速缓存),然后刷盘,可以保证内存与磁盘都有一份数据,访问时,直接从内存读取。
刷盘策略分为异步和同步两种。
在 Linux 操作系统中,为了加快文件的读写,当应用程序需要读取文件中的数据时,操作系统先分配一些内存,将数据从存储设备读入到这些内存中,然后再将数据分发给应用程序;当需要往文件中写数据时,操作系统先分配内存接收用户数据,然后再将数据从内存写到磁盘上。
文件 Cache 管理就是对这些由操作系统分配,并用来存储文件数据的内存的管理。
Cache 管理的优劣通过两个指标衡量:
metaq在文件读写操作上做了一定的优化,使用内存映射的方式完成读写,替代了传统的IO操作,从而大大的减少了文件读写系统调用的次数,提升了IO的性能。
传统的文件访问:
这种方式是非常低效的, 每一次I/O操作都需要一次系统调用。 另外, 如果若干个进程访问同一个文件, 每个进程都要在自己的地址空间维护一个副本, 浪费了内存空间
内存映射的方式:
首先建立好虚拟内存和磁盘文件之间的映射(mmap系统调用),当进程访问页面时产生一个缺页中断,内核将页面读入内存(也就是说把磁盘上的文件拷贝到内存中),并且更新页表指向该页面。
所有进程共享同一物理内存,物理内存中可以只存储一份数据,不同的进程只需要把自己的虚拟内存映射过去就可以了,这种方式非常方便于同一副本的共享,节省内存。
经过内存映射之后,文件内的数据就可以用内存读/写指令来访问,而不是用Read和Write这样的I/O系统函数,从而提高了文件存取速度。
metaq的整体架构如下图所示,主要包括Broker集群(metaq的服务端),client集群(发布者集群和订阅者集群),nameServer集群。
Broker分为master和slave。每个Broker与nameserver集群中的所有节点建立长连接,定时注册topic信息到所有的nameServer。
Producer与nameServer集群中的一个节点(随机)建立长连接,定期从nameServer 取topic路由信息,并向提供topic服务的master broker建立长连接,且定时向master发送心跳。Producer发布消息是发布到master,在由master同步到所有broker。
Consumer与nameServer集群中的一个节点建立长连接,定期从nameServer取topic的路由信息,并向提供topic服务的master、slave broker建立长连接,并定时向master、slave发送心跳。Comsumer既可以从slave订阅消息,也可以从master订阅消息。
一个消息从发送端应用,到消费端应用,中间有三个过程需要保证消息的可靠性。
1.发送端发消息
消息生产者发送消息后返回SendResult,如果isSuccess返回为true,则表示消息已经确认发送到服务器并被服务器接收存储。整个发送过程是一个同步的过程。保证消息送达服务器并返回结果。只有当消息中间件及时明确的返回成功,才能确认消息可靠到达消息中间件。
2.消息中间件把消息存储起来
metaq服务器收到消息后首先把消息存放在磁盘文件中,确保持久存储,写入成功之后返回应答给发布者。因此,可以确认每条发送结果为成功的消息服务器都是写入磁盘的。内存中内容属于非持久数据,会在断电之后丢失。
3.消费端消费消息
消费者是一条接着一条地顺序消费消息,只有在成功消费一条消息后才会接着消费下一条。如果在消费某条消息失败(如异常),则会尝试重试消费这条消 息(默认最大5次),超过最大次数后仍然无法消费,则将消息存储在消费者的本地磁盘,由后台线程继续做重试。而主线程继续往后走,消费后续的消息。。由此来保证消息的可靠消费。
metaq消息的传递,通信,是使用的netty,并在netty之上作了简单的协议封装。
网络协议如下:
数据部分采用json序列化。
1.消息推送
许多系统通过metaq进行消息推送
2.数据库同步
精卫是通过metaq发消息感知数据库binlog的变化,并进行数据库复制的。
精卫是阿里的一个数据库同步中间件。
精卫首先解析mysql的binlog,然后以消息的形式发往metaq,下游应用(比如终搜,TC,IC等)来消费Mysql数据库操作的变更事件完成数据库同步。整个过程,metaq通过提供严格的顺序消息,事务消费方式保证了数据的可靠,高效。
3.实时消息
IM对消息实时性要求极高,metaq目前在来往得到了广泛使用,包括注册通知、私信、扎堆分享,语音文字消息等功能在使用metaq。metaq使用长轮询拉模式,可保证消息同push方式一样实时,通常在几个毫秒。
notify | metaq |
---|---|
消息不保证100%有序 | 消息有序 |
push模式(服务端主动推) | pull模式(客户端主动拉) |
支持分布式事务 | 不支持 |
基于jms标准 | 基于消息队列 |
可以选择持久化or非持久化 | 所有消息都是持久化的 |
不支持消息回溯,只能重发 | 支持消息回溯 |
不支持集群内广播消息 | 支持(集群内每台机器都能收到消息) |
适合比较复杂的业务模型 | 轻量级,高性能 |
接入方式更灵活 | 接入方式不如Notify灵活 |