过去几年中,咱们一直在使用、构建和宣传消息队列,咱们认为它们是很使人敬畏的,这也不是什么秘密。咱们相信对任何架构或应用来讲,消息队列都是一个相当重要的组件,下面是十个理由:前端
1. 1解耦
在项目启动之初来预测未来项目会碰到什么需求,是极其困难的。消息队列在处理过程当中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这容许你独立的扩展或修改两边的处理过程,只要确保它们遵照一样的接口约束。java
消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ。消息中间件到底该如何使用,什么时候使用这是一个问题,胡乱地使用消息中间件增长了系统的复杂度,若是用很差消息中间件还不如不用。
php
1)点对点通信:点对点方式是最为传统和常见的通信方式,它支持一对1、一对多、多对多、多对一等多种配置方式,支持树状、网状等多种拓扑结构。
2)多点广播:MQ适用于不一样类型的应用。其中重要的,也是正在发展中的是"多点广播"应用,即可以将消息发送到多个目标站点(DestinationList)。可使用一条MQ指令将单一消息发送到多个目标站点,并确保为每一站点可靠地提供信息。MQ不只提供了多点广播的功能,并且还拥有智能消息分发功能,在将一条消息发送到同一系统上的多个用户时,MQ将消息的一个复制版本和该系统上接收者的名单发送到目标MQ系统。目标MQ系统在本地复制这些消息,并将它们发送到名单上的队列,从而尽量减小网络的传输量。
3)发布/订阅(Publish/Subscribe)模式:发布/订阅功能使消息的分发能够突破目的队列地理指向的限制,使消息按照特定的主题甚至内容进行分发,用户或应用程序能够根据主题或内容接收到所须要的消息。发布/订阅功能使得发送者和接收者之间的耦合关系变得更为松散,发送者没必要关心接收者的目的地址,而接收者也没必要关心消息的发送地址,而只是根据消息的主题进行消息的收发。在MQ家族产品中,MQEventBroker是专门用于使用发布/订阅技术进行数据通信的产品,它支持基于队列和直接基于TCP/IP两种方式的发布和订阅。
4)群集(Cluster):为了简化点对点通信模式中的系统配置,MQ提供Cluster(群集)的解决方案。群集相似于一个域(Domain),群集内部的队列管理器之间通信时,不须要两两之间创建消息通道,而是采用群集(Cluster)通道与其它成员通信,从而大大简化了系统配置。此外,群集中的队列管理器之间可以自动进行负载均衡,当某一队列管理器出现故障时,其它队列管理器能够接管它的工做,从而大大提升系统的高可靠性。
html
当你的应用上了Hacker News的首页,你将发现访问流量攀升到一个不一样寻常的水平。在访问量剧增的状况下,你的应用仍然须要继续发挥做用,可是这样的突发流量并不常见;若是为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列可以使关键组件顶住增加的访问压力,而不是由于超出负荷的请求而彻底崩溃。请查看咱们关于峰值处理能力的博客文章了解更多此方面的信息。mysql
当体系的一部分组件失效,不会影响到整个系统。消息队列下降了进程间的耦合度,因此即便一个处理消息的进程挂掉,加入队列中的消息仍然能够在系统恢复后被处理。而这种容许重试或者延后处理请求的能力一般是造就一个略感不便的用户和一个沮丧透顶的用户之间的区别。linux
在一个分布式系统里,要获得一个关于用户操做会用多长时间及其缘由的整体印象,是个巨大的挑战。消息系列经过消息被处理的频率,来方便的辅助肯定那些表现不佳的处理过程或领域,这些地方的数据流都不够优化。
不少时候,你不想也不须要当即处理消息。消息队列提供了异步处理机制,容许你把一个消息放入队列,但并不当即处理它。你想向队列中放入多少消息就放多少,而后在你乐意的时候再去处理它们。
如下介绍消息队列在实际应用中经常使用的使用场景。异步处理,应用解耦,流量削锋和消息通信四个场景。
场景说明:用户注册后,须要发注册邮件和注册短信。传统的作法有两种 1.串行的方式;2.并行方式
a、串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务所有完成后,返回给客户端。
b、并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差异是,并行的方式能够提升处理的时间
假设三个业务节点每一个使用50毫秒钟,不考虑网络等其余开销,则串行方式的时间是150毫秒,并行的时间多是100毫秒。
由于CPU在单位时间内处理的请求数是必定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)
小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?
引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构以下:
按照以上约定,用户的响应时间至关因而注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,所以写入消息队列的速度很快,基本能够忽略,所以用户的响应时间多是50毫秒。所以架构改变后,系统的吞吐量提升到每秒20 QPS。比串行提升了3倍,比并行提升了两倍。
场景说明:用户下单后,订单系统须要通知库存系统。传统的作法是,订单系统调用库存系统的接口。以下图:
传统模式的缺点:假如库存系统没法访问,则订单减库存将失败,从而致使订单失败,订单系统与库存系统耦合
如何解决以上问题呢?引入应用消息队列后的方案,以下图:
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操做
假如:在下单时库存系统不能正常使用。也不影响正常下单,由于下单后,订单系统写入消息队列就再也不关心其余的后续操做了。实现订单系统与库存系统的应用解耦
流量削锋也是消息队列中的经常使用场景,通常在秒杀或团抢活动中使用普遍。
应用场景:秒杀活动,通常会由于流量过大,致使流量暴增,应用挂掉。为解决这个问题,通常须要在应用前端加入消息队列。
a、能够控制活动的人数
b、能够缓解短期内高流量压垮应用
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面。
秒杀业务根据消息队列中的请求信息,再作后续处理
日志处理是指将消息队列用在日志处理中,好比Kafka的应用,解决大量日志传输的问题。架构简化以下
日志采集客户端,负责日志数据采集,定时写受写入Kafka队列
Kafka消息队列,负责日志数据的接收,存储和转发
日志处理应用:订阅并消费kafka队列中的日志数据
消息通信是指,消息队列通常都内置了高效的通讯机制,所以也能够用在纯的消息通信。好比实现点对点消息队列,或者聊天室等
点对点通信:
客户端A和客户端B使用同一队列,进行消息通信。
聊天室通信:
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现相似聊天室效果。
以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。
消息队列采用高可用,可持久化的消息中间件。好比Active MQ,Rabbit MQ,Rocket Mq。
(1)应用将主干逻辑处理完成后,写入消息队列。消息发送是否成功能够开启消息的确认模式。(消息队列返回消息接收成功状态后,应用再返回,这样保障消息的完整性)
(2)扩展流程(发短信,配送处理)订阅队列消息。采用推或拉的方式获取消息并处理。
(3)消息将应用解耦的同时,带来了数据一致性问题,能够采用最终一致性方式解决。好比主数据写入数据库,扩展应用根据消息队列,并结合数据库方式实现基于消息队列的后续处理。
分为Zookeeper注册中心,日志收集客户端,Kafka集群和Storm集群(OtherApp)四部分组成。
Zookeeper注册中心,提出负载均衡和地址查找服务
日志收集客户端,用于采集应用系统的日志,并将数据推送到kafka队列
Kafka集群:接收,路由,存储,转发等消息处理
Storm集群:与OtherApp处于同一级别,采用拉的方式消费队列中的数据
如下是新浪kafka日志处理应用案例:转自(http://cloud.51cto.com/art/201507/484338.htm)
(1)Kafka:接收用户日志的消息队列
(2)Logstash:作日志解析,统一成JSON输出给Elasticsearch
(3)Elasticsearch:实时日志分析服务的核心技术,一个schemaless,实时的数据存储服务,经过index组织数据,兼具强大的搜索和统计功能
(4)Kibana:基于Elasticsearch的数据可视化组件,超强的数据可视化能力是众多公司选择ELK stack的重要缘由
讲消息队列就不得不提JMS 。JMS(Java Message Service,Java消息服务)API是一个消息服务的标准/规范,容许应用程序组件基于JavaEE平台建立、发送、接收和读取消息。它使分布式通讯耦合度更低,消息服务更加可靠以及异步性。
在EJB架构中,有消息bean能够无缝的与JM消息服务集成。在J2EE架构模式中,有消息服务者模式,用于实现消息与应用直接的解耦。
在JMS标准中,有两种消息模型P2P(Point to Point),Publish/Subscribe(Pub/Sub)。
P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每一个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。
P2P的特色
每一个消息只有一个消费者(Consumer)(即一旦被消费,消息就再也不在消息队列中)
发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息以后,无论接收者有没有正在运行,它不会影响到消息被发送到队列
接收者在成功接收消息以后需向队列应答成功
若是但愿发送的每一个消息都会被成功处理的话,那么须要P2P模式。
包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。
Pub/Sub的特色
每一个消息能够有多个消费者
发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须建立一个订阅者以后,才能消费发布者的消息
为了消费消息,订阅者必须保持运行的状态
为了缓和这样严格的时间相关性,JMS容许订阅者建立一个可持久化的订阅。这样,即便订阅者没有被激活(运行),它也能接收到发布者的消息。
若是但愿发送的消息能够不被作任何处理、或者只被一个消息者处理、或者能够被多个消费者处理的话,那么能够采用Pub/Sub模型。
在JMS中,消息的产生和消费都是异步的。对于消费来讲,JMS的消息者能够经过两种方式来消费消息。
(1)同步
订阅者或接收者经过receive方法来接收消息,receive方法在接收到消息以前(或超时以前)将一直阻塞;
(2)异步
订阅者或接收者能够注册为一个消息监听器。当消息到达以后,系统自动调用监听器的onMessage方法。
JNDI:Java命名和目录接口,是一种标准的Java命名系统接口。能够在网络上查找和访问服务。经过指定一个资源名称,该名称对应于数据库或命名服务中的一个记录,同时返回资源链接创建所必须的信息。
JNDI在JMS中起到查找和访问发送目标或消息来源的做用。
通常商用的容器,好比WebLogic,JBoss,都支持JMS标准,开发上很方便。但免费的好比Tomcat,Jetty等则须要使用第三方的消息中间件。本部份内容介绍经常使用的消息中间件(Active MQ,Rabbit MQ,Zero MQ,Kafka)以及他们的特色。
ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个彻底支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已是好久的事情了,可是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。
ActiveMQ特性以下:
RabbitMQ是流行的开源消息队列系统,用erlang语言开发。RabbitMQ是AMQP(高级消息队列协议)的标准实现。支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX,持久化。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。结构图以下:
几个重要概念:
消息队列的使用过程,以下:
exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。
号称史上最快的消息队列,它实际相似于Socket的一系列接口,他跟Socket的区别是:普通的socket是端到端的(1:1的关系),而ZMQ倒是能够N:M 的关系,人们对BSD套接字的了解较多的是点对点的链接,点对点链接须要显式地创建链接、销毁链接、选择协议(TCP/UDP)和处理错误等,而ZMQ屏蔽了这些细节,让你的网络编程更为简单。ZMQ用于node与node间的通讯,node能够是主机或者是进程。
引用官方的说法: “ZMQ(如下ZeroMQ简称ZMQ)是一个简单好用的传输层,像框架同样的一个socket library,他使得Socket编程更加简单、简洁和性能更高。是一个消息处理队列库,可在多个线程、内核和主机盒之间弹性伸缩。ZMQ的明确目标是“成为标准网络协议栈的一部分,以后进入Linux内核”。如今还未看到它们的成功。可是,它无疑是极具前景的、而且是人们更加须要的“传统”BSD套接字之上的一 层封装。ZMQ让编写高性能网络应用程序极为简单和有趣。”
特色是:
高性能,非持久化
跨平台:支持Linux、Windows、OS X等
多语言支持; C、C++、Java、.NET、python等30多种开发语言
可单独部署或集成到应用中使用
可做为Socket通讯库使用
与RabbitMQ相比,ZMQ并不像是一个传统意义上的消息队列服务器,事实上,它也根本不是一个服务器,更像一个底层的网络通信库,在Socket API之上作了一层封装,将网络通信、进程通信和线程通信抽象为统一的API接口。支持“Request-Reply “,”Publisher-Subscriber“,”Parallel Pipeline”三种基本模型和扩展模型。
ZeroMQ高性能设计要点:
一、无锁的队列模型
对于跨线程间的交互(用户端和session)之间的数据交换通道pipe,采用无锁的队列算法CAS;在pipe两端注册有异步事件,在读或者写消息到pipe的时,会自动触发读写事件。
二、批量处理的算法
对于传统的消息处理,每一个消息在发送和接收的时候,都须要系统的调用,这样对于大量的消息,系统的开销比较大,zeroMQ对于批量的消息,进行了适应性的优化,能够批量的接收和发送消息。
三、多核下的线程绑定,无须CPU切换
区别于传统的多线程并发模式,信号量或者临界区, zeroMQ充分利用多核的优点,每一个核绑定运行一个工做者线程,避免多线程之间的CPU切换开销。
Kafka是一种高吞吐量的分布式发布订阅消息系统,它能够处理消费者规模的网站中的全部动做流数据。 这种动做(网页浏览,搜索和其余用户的行动)是在现代网络上的许多社会功能的一个关键因素。 这些数据一般是因为吞吐量的要求而经过处理日志和日志聚合来解决。 对于像Hadoop的同样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka的目的是经过hadoop的并行加载机制来统一线上和离线的消息处理,也是为了经过集群机来提供实时的消费。
Kafka是一种高吞吐量的分布式发布订阅消息系统,有以下特性:
Kafka相关概念
通常应用在大数据日志处理或对实时性(少许延迟),可靠性(少许丢数据)要求稍低的场景使用。
消息队列是一个能让你得到容错性,分布式,解耦等架构能力的系统。纸上谈兵的话,它看起来还不错。
或许消息列队在你的应用中有很多适用的场景。你能够看下这篇关于消息队列优势的文章,看看到底有哪些合适的场景。但可不要由于说"能解耦那太好了”就轻易使用它。咱们来看一个例子——你但愿你的邮件发送和订单处理互相解耦。
所以你发送一个消息到消息队列里,而后邮件处理系统取出这个消息并发送邮件。那你在一个独立的单classpath的应用中怎么实现呢?让你的订单处理服务依赖于一个邮件服务,而后调用sendEmail()方法,而不是sendToMQ()方法。若是你使用了消息队列,你须要定义一个两个系统都能识别的消息格式 ;若是你不使用消息队列,那么你得定义一个方法签名。它们有什么本质的区别吗?其实没有。
不过你可能还有别的消费者想要对某个指定的消息进行额外的处理?这的确是可能发生的,而并不仅是针对咱们这里说到的这个项目而已。尽管确有可能,但相比添加另外一个方法调用而言,它可能并不值当。耦合?是的。不过这个耦合并无什么不方便的。
那我应该如何处理峰值流量?你能够经过消息队列将请求放到一个持久化队列中,而后再一并处理它们。这是一个很是有用的特性,不过它也受限于几个 因素——你的请求是在UI后台处理,仍是须要即时响应?serlvet容器的线程池某种程度上能够看成是一个队列,用户最终会拿到响应,可是得须要等待(若是线程的超时时间太短的话,请求可能会丢失)。
你可使用一个内存队列来存储那些较重的请求(得在UI后台进行处理)。不过注意了,你的队列并非默认高可用的。好比说,若是一个消息队列节点挂掉了,你的消息就丢失了。所以,不去使用应用节点内的内存队列,而是去使用一个消息队列,这可能并无什么优点。
消息队列使得咱们能够进行异步处理——这的确是个有用的特性。你不但愿在用户等待的时候作一些很重的操做。不过你也可使用一个内存队列,或者简单地启动一个新的线程(好比spring的@Async注解)。这样又有另外一个问题——若是消息丢失的话是否有问题?若是你应用处理请求的节点挂了,你能够进行恢复吗?你会发现这事会常常发生,若是不保证全部消息都处理到的话,很难保证功能的正确性。所以,仅将较重的调用进行异步处理是比较可取的。
把消息放到队列以便让另外一个组件来进行处理,对于这个场景,若是消息丢失是没法接受的 ,这也有一个很简单的解决方案——数据库。你能够把一条processed=false的数据存储到数据库中。而后再运行一个调度做业,将全部未处理的记录挑选出来,异步地进行处理。当处理完成的时候,将标记设为true。我常常用这个方法,包括在一些大型的线上系统中,它也工做得挺好的。
这样你还能不断地对你的应用节点进行扩展,只要它们的内存中没有任何的持久化状态的话。无论你是否使用了消息队列均可以(临时的内存处理队列并不属于持久化状态)。
为何我要给常常用到的消息队列提供一些备选方案?由于若是你因为不恰当的缘由选择了它,那么消息队列可能会成为一个负担。它们并不是如想像中那样容易使用。首先,它有一个学习曲线。通常来讲,你集成的组件切分得越多,就越容易出现问题。其次,还有一个设置及配置的成本。好比说,当消息队列须要在一个集群中运行的话,好比说多个数据中心,那么这就变得复杂了。
高可用性并非上来就有的——默认它是不会打开的。还有就是你的应用节点如何链接到消息队列?经过一个刷新的链接池,或者使用短生命周期的DNS记录,仍是经过一个负载均衡器?你的队列可能还有许多配置项,大小是多少,行为是怎样的(消费者需不须要确认接受,要不要通知处理失败,多个消费者可以取到同一个消息吗,消息有没有TTL,等等)同时还有网络及消息传递的开销,尤为是如今你们都喜欢用XML或者JSON来传输消息。若是你过分地使用了消息队列,那么它会增长你系统的延时。
最后一点,但并非最次要的——若是出现问题的话,使用消息队列会让问题跟踪变得异常困难。你无法在IDE中看到所谓的调用层次,由于一旦你发送消息到队列里了,你就得本身去查找它在哪里处理的了。这可不是听起来那么简单的。你看到了吧,它会给你增长许多的复杂性,以及许多须要注意的东西。
一般而言,在某些上下文中,消息队列仍是很是有用的。当它们的确适合的话,我也会在项目中使用它们——比方说,咱们不想丢失消息,但又但愿能快速地进行处理。我也见过它在一些不太常见的场景中使用的状况,好比说只有一个应用节点来进行消费,无论是哪一个节点投递过来的消息。你还能够看下stackoverflow上的这个问题。还有一些使用场景就是,或许你的确须要进行多语言间的通讯,又或者你的数据流已通过于复杂了,不使用新的消息消费者而是增长新方法调用的话代价会很大。
我想说的是那句老掉牙的真理“杀鸡焉用牛刀”。若是你不是很肯定已经没有别的更容易管理和维护的方法,必定要使用消息队列的话,最好不要使用它。不要由于”万一它有用呢“而去用它——只有你确实以为须要的话再去使用。由于颇有可能,就像这里说到的这个项目同样,消息队列实际上是没有必要的。
选择可以支持消息持久化的MQ方案。如:ActiveMQ、RabbitMQ等,给消息一个处理状态如:process=false。
还可使用缓存方案作:如Redis,它自己也是支持持久化的。
消息队列MQ的原理及实现方法:http://blog.csdn.net/lzq_csdn_th/article/details/51945408
关于消息队列的使用:http://www.cnblogs.com/linjiqin/p/5720865.html
消息队列使用的四种场景介绍:http://blog.csdn.net/cws1214/article/details/52922267
使用消息队列的十个理由:http://www.oschina.net/translate/top-10-uses-for-message-queue?p=2
国外的一款消息队列IronMQ:http://iron.io/products/mq?rc=blog_mq_t10
你可能并不须要消息队列:http://kb.cnblogs.com/page/212710/