整理的OSChina第37期高手问答——消息队列服务,嘉宾为 @shaneyuan 。
@shaneyuan 现就职于广州 UC 公司,是 UCMQ 的作者,以下简称SY。
UCMQ是一款轻量的HTTP协议级消息队列服务组件,项目的最初原型来自 @张宴 的HTTPSQS。
基本特性:
消息队列简介及应用场景相关:
消息队列(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:置为多大是一个经验值来的。一般来说“生产者”突增大量消息,而“消费者”短时间无法处理完,这样消息就会停留在消息队列中,而停留的消息数是有限制的,超出此限制的消息将无法写入。但如果不限制则队列新进来的请求需要等未处理的请求处理完。