zmq和ucmq消息队列简述
乐钱青
2023-12-01
ZMQ是什么?
这是个类似于Socket的一系列接口,他跟Socket的区别是:普通的socket是端到端的(1:1的关系),而ZMQ却是可以N:M 的关系,人们对BSD套接字的了解较多的是点对点的连接,点对点连接需要显式地建立连接、销毁连接、选择协议(TCP/UDP)和处理错误等,而ZMQ屏蔽了这些细节,让你的网络编程更为简单。ZMQ用于node与node间的通信,node可以是主机或者是进程。
区别:
1、zmq套接字是异步的,可以实现特定模式
2、TCP是一对一,ZeroMQ是多对多的,可以根据套接字类型实现一对多,一对一,多对一,或多对多
3、ZeroMQ传输消息,TCP传输字节
4、ZeroMQ 隐藏IO细节,ZeroMQ不在乎目的是否存在
5、ZeroMQ可以往多个节点发送数据,可以从多个节点接收数据
ZMQ 的 hello world!
由 Client 发起请求,并等待 Server 回应请求。请求端发送一个简单的 hello,服务端则回应一个 world。请求端和服务端都可以是 1:N 的模型。通常把 1 认为是 Server ,N 认为是 Client 。ZMQ 可以很好的支持路由功能(实现路由功能的组件叫作 Device),把 1:N 扩展为N:M (只需要加入若干路由节点)。使用 ZMQ 写基本的程序的方法,需要注意的是:
a) 服务端和客户端无论谁先启动,效果是相同的,这点不同于 Socket。
b) 在服务端收到信息以前,程序是阻塞的,会一直等待客户端连接上来。
c) 服务端收到信息以后,会 send 一个“World”给客户端。值得注意的是一定是 client 连接上来以后,send 消息给 Server,然后 Server 再 rev 然后响应 client,这种一问一答式的。如果 Server 先 send,client 先 rev 是会报错的。
d) ZMQ 通信通信单元是消息,他除了知道 Bytes 的大小,他并不关心的消息格式。因此,你可以使用任何你觉得好用的数据格式。Xml、Protocol Buffers、Thrift、json 等等。
e) 虽然可以使用 ZMQ 实现 HTTP 协议,但是,这绝不是他所擅长的。
ZMQ 的 Publish-subscribe 模式
a) 与 Hello World 不同的是,Socket 的类型变成 SOCKET_PUB 和 SOCKET_SUB 类型。
b) 客户端需要$subscriber->setSockOpt (ZMQ::SOCKOPT_SUBSCRIBE, $filter);设置一个过滤值,相当于设定一个订阅频道,否则什么信息也收不到。
c) 服务器端一直不断的广播中,如果中途有 Subscriber 端退出,并不影响他继续的广播,当 Subscriber 再连接上来的时候,收到的就是后来发送的新的信息了。这对比较晚加入的,或者是中途离开的订阅者,必然会丢失掉一部分信息,这是这个模式的一个问题,所谓的 Slow joiner。稍后,会解决这个问题。
d) 但是,如果 Publisher 中途离开,所有的 Subscriber 会 hold 住,等待 Publisher 再上线的时候,会继续接受信息。
UCMQ是一款轻量的HTTP协议级消息队列服务组件,项目的最初原型来自 @张宴 的HTTPSQS。
基本特性:
支持标准的HTTP协议( GET/POST方法),支持长连接(keep-alive);
请求响应非常快速,入队列、出队列速度超过10000次/秒;
每个UCMQ实例支持多队列,队列通过操作接口自动创建;
单个队列默认限制存储100w未读消息,可以不限制(非必要建议限制);
可以在不停止服务的情况下便捷地修改单个队列的属性(大小限制/只读锁/延时队列/同步频率);
可以实时查看队列属性(入队列数量、出队列数量、未读消息数量、消息积压数量)。
每个队列有独立的数据文件易管理易搬迁。
消息队列简介及应用场景相关:
消息队列(Message Queue):把消息按照产生的次序加入队列,而由另外的处理程序/模块将其从队列中取出,并加以处理;从而形成了一个基本的消息队列。使用消息队列可以很好地将任务以异步的方式进行处理,或者进行数据传送和存储等。例如,当你频繁地向数据库中插入数据、频繁地向搜索引擎提交数据,就可采取消息队列来异步插入。另外,还可以将较慢/较复杂的处理逻辑、有并发数量限制的处理逻辑,通过消息队列放在后台处理。
常规的使用场景:短信服务、电子邮件服务、图片处理服务、好友动态推送服务等。
协议相关:
使用HTTP协议单纯考虑了跨语言/跨平台和易接入。我个人觉得UCMQ的性能不需要太过担忧,大家可以看相关测试数据:http://tech.uc.cn/?p=1344 前提是UCMQ只是一个单进程单线程的服务。如果实在对性能有极高要求可以部署多个实例。
周边方案对比:
1.UCMQ比起其它消息组件的长处和短处是什么。
SY:与其他消息队列相比,延续了:协议通用性好/性能高;扩展了:易用性/数据安全性高/内存消耗小(数据缓存随读写位置移动)/易搬迁(每个队列数据独立)/易维护(轻量级)/监控简单(可实时获取“所有/单独”队列状态信息)/添加了特色的队列服务(延时队列/队列写锁等)。
主要有价值的问答整理如下:
1、单个UCMQ无法满足性能要求怎么办?
SY:如果单个实例已经到达性能瓶颈,建议部署多个实例客户端已实现负载均衡机制(轮询或随机)。
2、一般消息队列里储存的数据格式是json吗? 消费消息的时候, 从队列中取出数据, 做相应的业务处理, 请问你们是使用crontab来做定时还是nohup呢?消费队列这块想多了解点。
SY:消息队列中的数据可以是任意格式,对于业务来说json是个不错的选择。消费者模块可以处理完后即时再去读取队列中的新消息,如果队列取空后服务端会返回特殊的标识,消费者模块通过识别该标识休眠读取线程,建议使用定期休眠机制(如:100ms)。
3、 任务分发策略,有订阅功能吗?
SY:UCMQ不支持订阅功能,业务不分发。相对于gearman UCMQ没有同步操作(即:生产者将消息写入队列后,队列将触发消费者来读取消息),在UCMQ中读写队列都是由客户端(包括:生产者和消费者)主动发起的,所以不是由消息队列分发。
4、发生异常时的处理流程是怎样?
SY:如果消息生产者成功的将消息写入队列后,该消息一直有效直到被消费者成功读取或者消息体被损坏。取出后的消息将不可再访问。
5、UCMQ是怎么减少IO消耗提高读写性能的?
SY:读写位置的数据都是缓存在内存中的,并随读写位置移动而移动。
6、如果不手工清理的情况下,数据量级变大后会不会对系统产生性能的影响?数据是如何进行清理的?
SY:后台数据存储是分文件存储的,已读完的数据文件将被清理,所以不会消耗存储资源。从存储设计(http://tech.uc.cn/?p=1344)了解到只缓存当前读和写的数据文件,性能不随数据量增大而下降的。
7、我曾短暂地使用过gearman的队列服务。
以下疑问请解答:
1. 任务分发策略(如何选择worker)
简单读了一下您github上的代码,是不是说您的队列入队时,就是按先后入队,然后mgr去等待所有已经注册的work的请求,然后按请求顺序理出队?或 是其它的算法策略?
2. 发生异常时的处理流程
如果work或mgr服务在运行中,出现异常,那异常前正在执行的任务将如何容错和重置?
3. 如何保证队列中的每一个任务,可以正常的执行完成
一般来说,您的见解是一个任务分发出去之后就被消费掉了(一次执行),如果执行时异常导至任务未能正常执行完,任务将消失?还是采用什么策略保障任务消费是可以正常完成的?是由消息队列服务来监控保障还是采用日志处理方式,由开发者后期自行重置触发呢?
4. 您提到了,您的队列是记录在文件系统中的,我是否可以理解,是保存在了hash后的目录的文本文件中?如果队列任务是较大并发的项目或系统中,是否会因为这块的IO瓶颈最后导致队列服务低效呢影响整体服务呢?如果存在这种可能性,您在设计时,是否有相应的解决方法呢?比如混合使用FS和基于Ram的NoSQL或是自己实现一个基于内存的可持久化的存储形式。
SY:1:从MQ自身来说,出队列是按照入队列的先后顺序的--保证时序性是MQ的一个基本要求。
2:如果异常前数据已经到达MQ,或者尚未从MQ中取出,那么数据将持续保持有效,异常恢复后可以继续正常使用。如果异常时数据尚未到达MQ,或者已经从MQ中取出,则该条数据会有丢失的可能(具体情况看各自的客户端的异常处理机制是否完善)。
3:是的。当任务从MQ中取出后,其执行的正确性、完整性、安全性由取出者保证。
4:任何需要做持久化的产品,最终瓶颈都逃不过磁盘io的限制。我们能做的是,根据木桶原理,确保系统中不出线其它比磁盘io更短的短板。如果确实需要高性能的同时提供持久化和安全性的保障,那么可以考虑使用ssd硬盘--实测表明性能提升相当明显。至于纯内存的MQ,我们不排除后续版本中增加的可能性。但是混合使用纯内存和持久化的话,会使使用者无法确保当前到底用的是纯内存模式还是持久化模式--这样,在使用者看来,这样的MQ既无法时刻保证安全性,也无法时刻提供高性能,所以这样的MQ是不可信赖的(不可信赖的产品,在稍微重要的场合下,基本上就等于是不可用的)。
8、消息队列只用过beanstalkd, 不知道和beanstalkd想比,有什么差异?能否做多像beanstalkd那样启动多个daemon客户端挂在并行等待处理消息?有没有实例可以展示下比如发送邮件,推送动态等的应用?
SY:你提到的beanstalkd据我的了解他是内存式的,数据不会持久化的。我那么回答也是表明其存储上的差异。对beanstalkd了解不深其他方面暂时做不了评价/对比。UCMQ是会将写入消息持久化的,实例重启或异常退出数据都不会丢失,即便是服务器宕机也只是丢失部分未持久化的数据。同时持久化间隔可配置。
9、您好,在实际开发过程中,我并没有遇到需要用消息队列的需求,对于消息队列我也只是停留在概念上, 我想问:消息队列的典型应用场景?对于高并发的请求使用消息队列是否能保证及时性。消息队列设计那哪些基本技术?
SY:消息队列是异步的所以及时性不能保证,至于使用到的技术可以阅读我写的博客(http://tech.uc.cn/?p=1344)或阅读相关代码(https://github.com/ucweb/ucmq)。
10、消息队列还没怎么接触,想问下,有什么应用场景会用到消息队列呢?消息队列可以解决那些问题咧??
SY:消息队列有自己的一定的特性:异步/顺序读写/高性能/协议简单。所以一般会用于解决大量的服务器端异步请求,同时可以实现服务端的负载均衡和业务的容灾。
11、消息队列产品有很多, 有的提供socket监听, 不支http协议访问, 有的支持http协议访问, 这2者有什么区别吗? 我是否可以理解消息现在很多消息队列都是一个nosql呢? 消息队列和nosql 最显著区别是什么?
SY:消息队列与NOSQL的不同还是挺显著的:首先,应用场景不同:nosql是高效的弱关系型数据存储,消息队列是一次性消费的顺序消息组件。其次,数据保持的差异:nosql的数据是持久或定时持久的,消息队列的数据随取出而失效,即一次消费。使用什么协议都行,用http考虑的是简单/易接入。
12、消息队列和用数据库做存储,然后取数据库内容做处理有什么区别吗?举个例子,发送邮件,我可以先把邮件内容存到是数据库,然后以扫描数据库依次发送。第二点,消息队列是多线程的吗?
SY:异步消息机制保证正确发送,不保证及时发送,短信就是这样 。 1.用数据库慢。2.多进程或多线程读取数据库时需要添加标记,否则会造成数据重复,使用消息队列不会出现此问题。
13、消息队列的长度设置为多大合适?一般是预分配还是动态分配的?
SY:置为多大是一个经验值来的。一般来说“生产者”突增大量消息,而“消费者”短时间无法处理完,这样消息就会停留在消息队列中,而停留的消息数是有限制的,超出此限制的消息将无法写入。但如果不限制则队列新进来的请求需要等未处理的请求处理完。