MetaQ学习笔记
是什么?
一款分布式的消息中间件。它的核心作用就是解耦、异步和并行。
异步解耦,比如商品中库存发生了变更,那么就要同步搜索,如果没有消息,可能就要RPC通讯调用,这样就产生了网络IO,远程调用的资源损耗,有了消息中间件,只需要发送一个消息就ok了。
另外实现的一个重要功能就是削峰填谷,比如一个秒杀系统,前端接受秒杀流量一般非常大,但是秒杀的时候需要处理的业务逻辑很复杂,可能需要检查库存等其他逻辑。后端可能每秒的处理逻辑只有每分钟2000次,上流可能瞬间来了每分钟10000的流量,那么对下游业务来说可能就是灾难性的。如果引入了消息中间件,我们把系统分开,接受前端秒杀流量的改成生产者,处理订单的从MQ订阅消息,这样针对前端大规模流量保护了后端的服务,因为对后端来说永远是一个平滑的过程,保证了后端的稳定。
包含Metaq Demo,Metaq部署结构,Producer/Consumer注册,Metaq消息推拉,底层通信,基本的存储,负载均衡,(局部)有序消息,事务消息。
MetaQ的物理部署结构
从图中可以看出MetaQ主要包括四个集群:Name Server、Porducer、Consumer、Broker(又分为master与Slave)。
-
nameserver:维护了Topic与broker集群的对应关系,决定哪个topic路由到哪个broker集群。在broker 注册topic信息的时候,写入所有节点。
-
broker:Broker 分为Master 与Slave,一个Master 可以对应多个Slave,但是一个Slave 只能对应一个Master,Master 与Slave 的对应关系通过指定相同的BrokerName,不同BrokerId 来定义。Master 也可以部署多个。每个Broker 与Name Server 集群中的所有节点建立长连接,定时注册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订阅消息,订阅规则由Broker 配置决定。
存储消息
MetaQ将消息存储在本地文件中,每个文件最大大小为1G,如果写入新的消息时,超过当前文件大小,则会自动新建一个文件。文件名称为起始字节大小。以起始字节大小命名并排序这些文件是有诸多好处的,当消费者要抓取某个起始偏移量开始位置的数据,会变的很简单,只要根据传上来的offset二分查找文件列表,定位到具体文件,然后将绝对offset减去文件的起始节点转化为相对offset,即可开始传输数据.
这样做的好处如下:
-
队列轻量化,单个队列数据量非常少。
-
对磁盘的访问串行化,避免磁盘竞争,不会因为队列增加导致IOWAIT增高。
每个方案都有缺点,它的缺点如下:
-
写虽然完全是顺序写,但是读却变成了完全的随机读。
-
读一条消息,会先读Consume Queue,再读Commit Log,增加了开销。
-
要保证Commit Log与Consume Queue完全的一致,增加了编程的复杂度。
以上缺点如何克服:
-
随机读,尽可能让读命中PAGECACHE,减少IO读操作,所以内存越大越好。如果系统中堆积的消息过多,读数据要访问磁盘会不会由于随机读导致系统性能急剧下降,答案是否定的。
(1)、访问PAGECACHE时,即使只访问1k的消息,系统也会提前预读出更多数据,在下次读时,就可能命中内存。
(2)、 随机访问Commit Log磁盘数据,系统IO调度算法设置为NOOP(不是ANTICIPATORY吗)方式,会在一定程度上将完全的随机读变成顺序跳跃方式。
-
由于Consume Queue存储数据量极少,而且是顺序读,在PAGECACHE预读作用下,Consume Queue的读性能几乎与内存一致,即使堆积情况下。所以可认为Consume Queue完全不会阻碍读性能。
-
Commit Log中存储了所有的元信息,包含消息体,类似于Mysql、Oracle的redolog,所以只要有Commit Log在,Consume Queue即使数据丢失,仍然可以恢复出来。
存储结构
MetaQ的存储结构=物理队列+逻辑队列,这与Kafka的存储方式有点类似。
物理队列只有一个,采用固定大小的文件顺序存储消息。逻辑队列有多个,每个逻辑队列有多个分区,每个分区有多个索引。
a.消息顺序写入物理文件里面,每个文件达到一定的大小,新建一个文件继续顺序写数据(消息的写入是串行的,避免了磁盘竞争)。
b.消息的索引则顺序的写入逻辑文件中,并不存放真正的消息,只是存放指向消息的索引。
MetaQ对于客户端展现的是逻辑队列就是消费队列,consumer从消费队列里顺序取消息进行消费。
这种设计是把物理和逻辑分离,消费队列更加轻量化。所以MetaQ可以支撑更多的消费队列数,提升消息的吞吐量,并且有一定的消息堆积能力。
缺点 :
写虽然是顺序写,但是读却是随机读的
解决办法 :
尽可能让读命中pageCache,减少磁盘IO次数 (参考下文所述:Linux的文件Cache管理)
MetaQ的所有消息都是持久化的,先写入系统PAGECACHE(页高速缓存),然后刷盘,可以保证内存与磁盘都有一份数据,访问时,直接从内存读取。
刷盘策略分为异步和同步两种。
局部有序
MetaQ的一大特性就是能保证消息的局部有序。在负载均衡中提到,对于乱序消息采用轮询的方式分配到各个队列中,而对于顺序消息,在发布时我们可以实现一个select将需要保证顺序的消息都发往一个队列中这样做可以使得消费端在拉取到消费队列消费时按照规定的消息顺序进行消费,从而保证了消息的有序性。
send(msg,new MessageQueueSelector(){ @Override public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {} }, arg)
多个消费者消费顺序的问题
一般来说,消息队列都会保证queue当中的消息的顺序。然而如果有多个consumer同时消费同一个queue,那么这时就不能保证的消息的顺序性。
有时候,消息的顺序是非常重要的,为了能顺序的消费消息,我们只能启动一个consumer来消费这个queue。但是这样做的问题就是,如果这个consumer宕调了话,消息就不能得到处理了,consumer的可用性不能得到保证。
那么如何提高consumer的可用性那,通常的做法是启动多个consumer,让其中一个consumer成为master,其他的为slave,成为master的consumer才去消费queue中的消息(通常的做法是使用zookeeper进行选主)。
分布式事务
采用的是基于消息中间件的2PC方案,详情见另外一篇文章
零拷贝
在broker向消费端发送消息时,若采用传统的IO方式会从磁盘拷贝数据到页缓存->用户空间从页缓存读数据->用户空间再将数据写入socket缓存中->通过网络发送数据,显然这样做使得用户空间和内核空间产生了多余的读写。MetaQ采用零拷贝的方式,通过mmap的方式使得页缓存与用户空间缓存共享数据,之后直接将数据从页缓存中将数据传入socket缓存中加快效率。
系统mmap函数
mmap将一个文件或者其它对象映射进内存,文件以mmap的方式映射到用户的虚拟内存空间,省去了从内核缓冲区复制到用户空间的过程,可直接使用。文件需要以PAGE_SIZE为单位进行映射,而内存也只能以页为单位进行映射,若要映射非PAGE_SIZE整数倍的地址范围,要先进行内存对齐,强行以PAGE_SIZE的倍数大小进行映射。
mmap把文件映射到用户空间里的虚拟内存,文件中的位置在虚拟内存中有了对应的地址,可以像操作内存一样操作这个文件,相当于已经把整个文件放入内存,但在真正使用到这些数据前却不会消耗物理内存,也不会有读写磁盘的操作,只有真正使用这些数据时,也就是图像准备渲染在屏幕上时,虚拟内存管理系统VMS才根据缺页加载的机制从磁盘加载对应的数据块到物理内存进行渲染。这样的文件读写文件方式少了数据从内核缓存到用户空间的拷贝,效率很高。
优点
-
顺序性:metaq可以支持顺序消息,可以做到局部有序(Metaq可以保证,在单线程内使用该producer发送的消息按照发送的顺序达到服务器并存储,并按照相同顺序被消费者消费,前提是这些消息发往同一台服务器的同一个分区。)
-
实时性:采用长轮询+pull消费消息,metaq可以让用户自己决定如何在响应性和吞吐量之间做平衡,通过合理的配置参数可以获得较高的响应时间,实时性不低于push方式
-
提供了丰富的消息拉取模式,支持按时间回溯、定时消费、按条件过滤等多种消息拉取模式
-
支持10亿级别的消息堆积,不会因为消息堆积导致性能明显下降
-
高效的订阅者水平扩展机制,支持多组订阅者按需分配队列机器
缺点
-
消息重复问题(由于网络等原因):Metaq对消息重复的保证只能说在正常情况下保证不重复,异常情况下无法保证
-
metaq是不支持分布式事务的
-
metaq消息过滤功能扩展比较单一