目前主流的MQ前端
1.ZeroMQspring
号称最快的消息队列系统,尤为针对大吞吐量的需求场景。数据库
扩展性好,开发比较灵活,采用C语言实现,实际上只是一个socket库的从新封装,若是作为消息队列使用,须要开发大量的代码。ZeroMQ仅提供非持久性的队列,也就是说若是down机,数据将会丢失。其中,Twitter的Storm中使用ZeroMQ做为数据流的传输。服务器
2.RabbitMQ网络
结合erlang语言自己的并发优点,支持不少的协议:AMQP,XMPP, SMTP, STOMP,也正是如此,使的它变的很是重量级,更适合于企业级的开发。并发
性能较好,可是不利于作二次开发和维护。负载均衡
3.ActiveMQsocket
历史悠久的开源项目,是Apache下的一个子项目。已经在不少产品中获得应用,实现了JMS1.1规范,能够和spring-jms轻松融合,实现了多种协议,不够轻巧(源代码比RocketMQ多),支持持久化到数据库,对队列数较多的状况支持很差。分布式
4.Redisoop
作为一个基于内存的K-V数据库,其提供了消息订阅的服务,能够看成MQ来使用,目前应用案例较少,且不方便扩展。对于RabbitMQ和Redis的入队和出队操做,各执行100万次,每10万次记录一次执行时间。
入队时,当数据比较小时Redis的性能要高于RabbitMQ,而如 果数据大小超过了10K,Redis则慢的没法忍受;出队时,不管数据大小,Redis都表现出很是好的性能,而RabbitMQ的出队性能则远低于 Redis。
5.Kafka/Jafka
Kafka是Apache下的一个子项目,是一个高性能跨语言分布式发布/订阅消息队列系统,而Jafka是在Kafka之上孵化而来的,即Kafka的一个升级版。
具备如下特性:
快速持久化,能够在O(1)的系统开销下进行消息持久化;
高吞吐,在一台普通的服务器上既能够达到10W/s的吞吐速率;彻底的分布式系统,Broker、Producer、Consumer都原生自动支持分布式,自动实现负载均衡;
支持Hadoop数据并行加载,对于像Hadoop的同样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。
Kafka经过Hadoop的并行加载机制统一了在线和离线的消息处理。Apache Kafka相对于ActiveMQ是一个很是轻量级的消息系统,除了性能很是好以外,仍是一个工做良好的分布式系统。
消息队列使用场景
1.解耦
解耦是消息队列要解决的最本质问题。所谓解耦,简单点讲就是一个事务,只关心核心的流程。而须要依赖其余系统但不那么重要的事情,有通知便可,无需等待结果。换句话说,基于消息的模型,关心的是“通知”,而非“处理”。
举一个例子,关于订单系统,订单最终支付成功以后可能须要给用户发送短信积分什么的,但其实这已经不是咱们系统的核心流程了。
若是外部系统速度偏慢(好比短信网关速度很差),那么主流程的时间会加长不少,用户确定不但愿点击支付过好几分钟才看到结果。那么咱们只须要通知短信系统“咱们支付成功了”,不必定非要等待它当即处理完成。
2.最终一致性
最终一致性指的是两个系统的状态保持一致,要么都成功,要么都失败。
固然有个时间限制,理论上越快越好,但实际上在各类异常的状况下,可能会有必定延迟达到最终一致状态,但最后两个系统的状态是同样的。
业界有一些为“最终一致性”而生的消息队列,如:
Notify(阿里)
QMQ(去哪儿)等
其设计初衷,就是为了交易系统中的高可靠通知。
以一个银行的转帐过程来理解最终一致性,转帐的需求很简单,若是A系统扣钱成功,则B系统加钱必定成功。反之则一块儿回滚,像什么都没发生同样。
然而,这个过程当中存在不少可能的意外:
A扣钱成功,调用B加钱接口失败。
A扣钱成功,调用B加钱接口虽然成功,但获取最终结果时网络异常引发超时。
A扣钱成功,B加钱失败,A想回滚扣的钱,但A机器down机。
可见,想把这件看似简单的事真正作成,真的不那么容易。
全部跨VM的一致性问题,从技术的角度讲通用的解决方案是:
强一致性,分布式事务,但落地太难且成本过高,后文会具体提到。
最终一致性,主要是用“记录”和“补偿”的方式。在作全部的不肯定的事情以前,先把事情记录下来,而后去作不肯定的事情,结果多是:成功、失败或是不肯定,“不肯定”(例如超时等)能够等价为失败。成功就能够把记录的东西清理掉了,对于失败和不肯定,能够依靠定时任务等方式把全部失败的事情从新搞一遍,直到成功为止。
回到刚才的例子,系统在A扣钱成功的状况下,把要给B“通知”这件事记录在库里(为了保证最高的可靠性能够把通知B系统加钱和扣钱成功这两件事维护在一个本地事务里),通知成功则删除这条记录,通知失败或不肯定则依靠定时任务补偿性地通知咱们,直到咱们把状态更新成正确的为止。
整个这个模型依然能够基于RPC来作,但能够抽象成一个统一的模型,基于消息队列来作一个“企业总线”。
具体来讲,本地事务维护业务变化和通知消息,一块儿落地(失败则一块儿回滚),而后RPC到达broker,在broker成功落地后,RPC返回成功,本地消息能够删除。不然本地消息一直靠定时任务轮询不断重发,这样就保证了消息可靠落地broker。
broker往consumer发送消息的过程相似,一直发送消息,直到consumer发送消费成功确认。
咱们先不理会重复消息的问题,经过两次消息落地加补偿,下游是必定能够收到消息的。而后依赖状态机版本号等方式作判重,更新本身的业务,就实现了最终一致性。
最终一致性不是消息队列的必备特性,但确实能够依靠消息队列来作最终一致性的事情。
另外,全部不保证100%不丢消息的消息队列,理论上没法实现最终一致性。好吧,应该说理论上的100%,排除系统严重故障和bug。
像Kafka一类的设计,在设计层面上就有丢消息的可能(好比定时刷盘,若是掉电就会丢消息)。哪怕只丢千分之一的消息,业务也必须用其余的手段来保证结果正确。
3.广播
消息队列的基本功能之一是进行广播。
若是没有消息队列,每当一个新的业务方接入,咱们都要联调一次新接口。有了消息队列,咱们只须要关心消息是否送达了队列,至于谁但愿订阅,是下游的事情,无疑极大地减小了开发和联调的工做量。
好比本文开始提到的产品中心发布产品变动的消息,以及景点库不少去重更新的消息,可能“关心”方有不少个,但产品中心和景点库只须要发布变动消息便可,谁关心谁接入。
4.错峰与流控
试想上下游对于事情的处理能力是不一样的。
好比,Web前端每秒承受上千万的请求,并非什么神奇的事情,只须要加多一点机器,再搭建一些LVS负载均衡设备和Nginx等便可。
但数据库的处理能力却十分有限,即便使用SSD加分库分表,单机的处理能力仍然在万级。因为成本的考虑,咱们不能奢求数据库的机器数量追上前端。
这种问题一样存在于系统和系统之间,如短信系统可能因为短板效应,速度卡在网关上(每秒几百次请求),跟前端的并发量不是一个数量级。
但用户晚上个半分钟左右收到短信,通常是不会有太大问题的。若是没有消息队列,两个系统之间经过协商、滑动窗口等复杂的方案也不是说不能实现。
但系统复杂性指数级增加,势必在上游或者下游作存储,而且要处理定时、拥塞等一系列问题。并且每当有处理能力有差距的时候,都须要单独开发一套逻辑来维护这套逻辑。因此,利用中间系统转储两个系统的通讯内容,并在下游系统有能力处理这些消息的时候,再处理这些消息,是一套相对较通用的方式。
消息队列使用总结
1.消息队列不是万能的,对于须要强事务保证并且延迟敏感的,RPC是优于消息队列的。
2.对于一些无关痛痒,或者对于别人很是重要可是对于本身不是那么关心的事情,能够利用消息队列去作。
3.支持最终一致性的消息队列,可以用来处理延迟不那么敏感的“分布式事务”场景,并且相对于笨重的分布式事务,多是更优的处理方式。
4.当上下游系统处理能力存在差距的时候,利用消息队列作一个通用的“漏斗”,在下游有能力处理的时候,再进行分发。
5.若是下游有不少系统关心你的系统发出的通知的时候,果断地使用消息队列吧。