Thursday, January 14, 2016

中间件学习――metaq-博客-云栖社区



中间件学习――metaq-博客-云栖社区

metaq是什么?

是一个基于"发布-订阅"的队列模型消息中间件,服务端使用JAVA编写,客户端支持JAVA、C++。
对外已开源,名字叫RocketMQ。

metaq的特性

消费模型

metaq采用发布-订阅模型,发布者发布消息到metaq,订阅者向metaq订阅消息。
消息的消费方式是pull方式,由消费者主动从metaq服务器拉取数据,解析成消息并消费。

消息持久性

metaq 接收到消息之后,会先把消息持久化到本地。
常用的持久化方式:

  • 持久化到DB
  • 持久化到KV存储,如levelDB,伯克利DB
  • 持久化到文件

metaq使用的是 持久化到文件 ,并充分利用Linux文件系统内存cache来提高性能。
注: 持久化部分的性能会直接影响消息中间件的性能。

消息堆积能力:metaq每台服务器提供大约亿级的消息堆积能力(多个业务方共用),超过堆积阈值,订阅消息吞吐量会下降。

消息过滤

对于应用比较多,访问量比较大的情况,消息量也就随之增大,一方面服务端给每个客户端发送消息时,总不能把全站的消息都发送过去,这样大量的无用消息在网络上传输是一种资源浪费。另一方面,客户端也不需要接收所有的消息,而只需要接收自己需要的消息。这时,消息中间件就需要一个消息过滤的功能。

metaq支持两种过滤方式:服务器端过滤,客户端过滤。

  • 服务器端过滤: 优点 是减少网络上无用消息的传输, 缺点 是增加服务端负担,实现复杂
  • 客户端过滤: 优点 是可以完全根据自己的需要定制哪些需要哪些不要, 缺点 是很多无用的消息要传输到客户端。如果客户端对应的应用不足以支撑这么多消息,就会造成应用的所有计算资源全部都在处理这些消息,甚至拖垮整个应用。

消息实时性

metaq客户端通过长轮询的方式连接服务端,可以保证消息非常实时,实时性不低于push

每个消息至少投递一次

Consumer先pull消息到本地,消费完之后,才会向服务器返回。
为了追求性能,metaq并不保证消息不重复发送,但是正常情况下很少出现。只有网络异常,consumer启动、停止等异常情况下才会出现重复。
本质原因是网络调用的不确定性,即会出现既不成功也不失败的第三种状态。

保证消息局部有序

通过队列的特性,保证消息的顺序。
发送端,将需要保证顺序的消息发送到同一个队列中。消费端,从队列里取消息,顺序消费。
不同的几组消息,可以发送到不同的队列中,提高并行性。

metaq的存储结构

metaq的逻辑存储结构是一种物理队列+逻辑队列的结构。

screenshot

物理队列只有一个,采用固定大小的文件顺序存储消息。逻辑队列有多个,每个逻辑队列有多个分区,每个分区有多个索引。
a.消息顺序写入物理文件里面,每个文件达到一定的大小,新建一个文件继续顺序写数据(消息的写入是串行的,避免了磁盘竞争)。
b.消息的索引则顺序的写入逻辑文件中,并不存放真正的消息,只是存放指向消息的索引。
metaq对于客户端展现的是逻辑队列就是消费队列,consumer从消费队列里顺序取消息进行消费。

这种设计是把物理和逻辑分离,消费队列更加轻量化。所以metaq可以支撑更多的消费队列数,提升消息的吞吐量,并且有一定的消息堆积能力。
缺点 :
写虽然是顺序写,但是读却是随机读的
解决办法 :尽可能让读命中pageCache,减少磁盘IO次数 (参考下文所述:Linux的文件Cache管理) 
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架构&消息的收发

metaq的整体架构如下图所示,主要包括Broker集群(metaq的服务端),client集群(发布者集群和订阅者集群),nameServer集群。

screenshot

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消息底层通信组件

metaq消息的传递,通信,是使用的netty,并在netty之上作了简单的协议封装。

网络协议如下:

screenshot

数据部分采用json序列化。

metaq的主要应用场景

1.消息推送

许多系统通过metaq进行消息推送

2.数据库同步

精卫是通过metaq发消息感知数据库binlog的变化,并进行数据库复制的。

精卫是阿里的一个数据库同步中间件。

精卫首先解析mysql的binlog,然后以消息的形式发往metaq,下游应用(比如终搜,TC,IC等)来消费Mysql数据库操作的变更事件完成数据库同步。
整个过程,metaq通过提供严格的顺序消息,事务消费方式保证了数据的可靠,高效。

3.实时消息

IM对消息实时性要求极高,metaq目前在来往得到了广泛使用,包括注册通知、私信、扎堆分享,语音文字消息等功能在使用metaq。
metaq使用长轮询拉模式,可保证消息同push方式一样实时,通常在几个毫秒。

metaq与notify的对比

notify metaq
消息不保证100%有序 消息有序
push模式(服务端主动推) pull模式(客户端主动拉)
支持分布式事务 不支持
基于jms标准 基于消息队列
可以选择持久化or非持久化 所有消息都是持久化的
不支持消息回溯,只能重发 支持消息回溯
不支持集群内广播消息 支持(集群内每台机器都能收到消息)
适合比较复杂的业务模型 轻量级,高性能
接入方式更灵活 接入方式不如Notify灵活

Push or Pull:

  • Push模式:很难掌握消息推送的时机和速率,因为consumer的消费速率不同。
  • Pull模式:consumer可以根据自己的状况选择拉取消息的时机和速率,缺点在于如果服务端没有可供消费的消息,将导致consumer不断轮询,浪费资源。

Read full article from 中间件学习――metaq-博客-云栖社区

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts