概述

消息队列已经逐渐成为企业IT系统内部通信的核心手段,它具有低耦合,可靠投递,广播,流量控制,最终一致性等一系列的功能,成为一部RPC(远程过程调用)的主要手段之一,当今市面上有很多主流的消息中间件,如老牌的ActiveMQ,RabbitMQ,炙手可热的Kafka,阿里巴巴自主研发的RocketMQ

组成

broker
消息服务器,作为server提供消息核心服务
Producer
消息生产者,业务的发起方,负责生产消息传输给broker
consumer
消息消费者,业务处理方,负责从brocker获取消息并进行业务逻辑处理
Topic
主题,发布订阅模式下的消息统一汇集地,不同生产者向topic发送消息,有MQ服务器分发到不同的订阅者,实现消息的广播
Queue
队列,PTP模式(点对点)下,特定生产者向特定queue发送消息,消费者订阅特定的queue完成指定消息的接受
Message
消息体根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传递

模式分类

点对点:
PTP点对点,使用queue作为通信载体
说明:
消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息
消息被消费以后,queue中不在存储,所以消息消费者不可能消费到已经被消费的消息,queue支持存在多个消费者,但是对于一个消息而言,只会有一个消费者可以消费,
MQ - 图1
发布订阅:
Pub/Sub 发布订阅(广播),使用Topic作为通信载体
说明:
消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息,和点对点方式不同,发布到topic的消息会被所有订阅者消费

queue实现了负载均衡,将producer生产的消息发送到消息队列中,由多个消费者消费,但一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存知道有一个可用的消费者,
topic实现了发布和订阅,当你发布一个消息,所有订阅这个topic的服务都能得到这个消息,所以从1到N个订阅者都能得到一个消息的拷贝

优势

系统解耦
交互系统之间没有直接的调用关系,只是通过消息传递,故系统侵入性不强,耦合度低
提高系统的响应时间
假如原来的一套逻辑,完成支付可能涉及先修改订单状态,计算用户积分,通知物流配送几个逻辑才能完成,通过MQ架构,就可将紧急重要(需要立刻响应)的业务放到该调用方法中,响应要求不高的使用消息队列,放到MQ队列中,供消费者处理
为大数据处理架构,提供服务
通过消息作为整合,大数据从的背景下,消息队列还与实时处理架构整合,为数据处理提供行性能支持
JAVA消息服务-JMS
JAVA消息服务应用程序接口时一个JAVA平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信
JMS中的P2P和Pub/Sub消息模式:点对点,和发布订阅最初是由JMS定义的,这两种模式主要区别或解决问题就是发送到队列的消息能否重复消费(多订阅)

场景

异步通信
有一些业务不想也不需要立即处理消息,消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它,想向队列中放入多少消息就放多少,然后在需要的时候再去处理他们
解耦
减低工程间的强依赖程度,针对异构系统进行适配,在项目启动之初来预测将来项目会碰到什么需求,是及其困难的,通过消息系统在处理过程中插入了一个隐含的,基于数据的接口层,两边的处理过程都需要实现这一接口,当应用发生变化时,可以独立的扩展或修改两边的处理过程,只要确保他们遵守同样的接口约束
冗余
有些情况下,处理数据的过程会失败,除非数据被持久化,否则将造成丢失,消息队列把数据进行持久化知道他们已经被处理完成,通过这一方式规避了数据丢失的风险,许多消息队列所采用的“插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理系统明确指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕
扩展性
因为消息队列解耦了你的处理过程所以增大消息入队和出队的频率是很容易的,只要另外增加处理过程即可,不需要改变代码,不需要调节参数,便于分布式扩容
过载保护
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量无法提前预知,如果一味为了能处理这类瞬间峰值访问为标准来投入资源随时待命无疑是巨大的浪费,使用消息队列能够是关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃
可恢复性
系统的一部分组件失效时,不会影响到整个系统,消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列的消息仍然可以在系统恢复后被处理,
顺序保证
在大多使用场景下,数据处理的顺序都很重要,大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理
缓冲
在任何重要的系统中,都会有需要不同的处理时间的元素,消息队列通过一个缓冲层来帮助任务最高效率的执行,该缓冲有助于控制和优化数据流经过系统的速度,以调节系统响应时间
数据流处理
分布式系统产生的海量数据流,如:业务日志,监控数据,用户行为等,针对这些数据流,进行实时或批量采集汇总,然后进行大数据分析是当前互联网的必备技术,通过消息队列完成此类数据收集是最好的选择

缺点

系统复杂性
多了一个中间件,需要为何,解耦,之类的
数据一致性
比如下单发短信,直接添加队列,不管队列执行结果
可用性
玩意挂了,就都停止了,需要保证高可用

常用协议

AMQP协议
AMQP即Advanced Message Queuing Protocol,,一个提供统一消息服务的应用层标准,高级消息队列协议,是应用层协议的一个开放标准,为面对消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制
优点:可靠,通用
MQTT协议
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分,该协议支持所有平台,几乎可以把所有联网物品与外界连接起来,被用来当做传感器和制动器(比如通过Twitten让房屋联)的通讯协议,
优点:格式简介,占用宽带小,移动端通信,PUSH,嵌入式系统
STOMP协议
STOMP(Streaming Text Orientated Message Protocol)是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面对消息的中间件)设计的简单文本协议,STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互
优点:命令模式(非topic\queue模式)
XMPP协议
XMPP(可扩展消息处理现场协议,Extensible Messaging and Presence Protocol)是基于可扩展标记 语言(XML)的协议,多用于即时消息(IM)以及在线现场探测,适用于服务器之间的准即时操作,核心是基于XML流传输,整个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不用
优点:通用公开,兼容性强,可扩展,安全性高,但XML编码格式占用宽带大
其他基于TCP/IP自定义的协议
有一些特殊框架(如redis,KAfka,zeroMq等)根据自身需要未严格遵循MQ规范,而是基于TCP/IP自行封装了一套协议,通过网络socket接口进行传输,实现了MQ的功能

常见消息中间件MQ

RocketMQ

阿里系下开源的一款分布式,队列模型的消息中间件,原名Metaq,3.0版本名称改为RocketMQ,是阿里参照kafka设计思想使用java实现的一套mq,同时将阿里系内部多款mq产品(Notify,metaq)进行整合,只维护核心功能,去除了所有其他运行时依赖,保证核心功能最简化,在此基础上配合阿里上述其他开源产品实现不同场景下mq的架构,目前主要多用于订单交易系统
特点
能够保证严格的消息顺序
提供针对消息的过滤功能
提供丰富的消息拉去模式
高效的订阅水平扩展能力
实时的消息订阅机制
亿级消息堆积能力
官方提供了一些不同于kafka的对比差异:https://rocketmq.apache.org/docs/motivation/

RabbitMQ

使用Erlang编写的一个开源的消息队列,本身支持很多的协议:AMQP,XMPP,SMTP,STOMP,也正是如此,使得它变得非常重量级,更适合于企业级的开发,同时实现了Broker架构,核心思想是生产者不会将消息直接发送给队列,消息在发送给客户端时先在中心队列排队,对路由(Routing),负载均衡(Load,balabce),数据持久化都有很好的支持,多用于进行企业级的ESB整合

ActiveMQ

apache下的一个子项目,使用java完全支持JMS1.1和J2EE1.4规范的JMS Provider实现的,少量代码就可以高效地实现高级应用场景,可插拔的传输协议,比如:in-VM, TCP, SSL, NIO, UDP, multicast, JGroups and JXTA transports。
RabbitMQ、ZeroMQ、ActiveMQ均支持常用的多种语言客户端C++,Java,Python.PHP等

redis

使用C语言开发的一个key-value的nosql数据库,开发维护很活跃,虽然它是一个key-value数据库存储系统,但是它本身支持MQ功能,所以完全可以当作一个轻量级的队列服务来使用,对于RabbitMQ和Redis的入队和出队操作,个执行100万次,没10万次记录一次执行时间,测试数据分别为 128Bytes、512Bytes、1K和10K四个不同大小的数据,实验表明:入队时,当数据比较小时redis的性能高于rabbitMQ。而如果数据大小超过10K,Redis则慢的无法忍受,出队时,无论数据大小,redis都表现出很好的性能,而rabbitMQ的出队性能远低于redis

Kafka

Apache下的一个子项目,使用scala实现的一个高性能分布式Publist/Subscribe消息队列系统
特性
快速持久化:通过磁盘顺序读写于零拷贝机制,可以在O(1)的系统开销下进行消息持久化
高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率
高堆积:支持topic下消费者较长时间离线,消息堆积量大,
完全的分布式系统:Broker、Producer、Consumer都原生自动支持分布式,依赖zookeeper自动实现复杂均衡
支持Hadoop数据库并行加载:对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案

ZeroMQ

号称最快的消息队列系统,专门为高吞吐量/低延迟的场景开发,在金融界的应用中经常使用,偏重于实时数据通信场景,ZMQ能够实现RabbitMQ不擅长的高级/复杂的队列,但是开发人员需要自己组合多种技术框架,开发成本高,因为ZeroMQ,具有一个独特的非中间件的模式,更像一个socket library ,你不需要安装和运行一个消息服务器或者中间件,因为你的应用程序本身就是使用ZeroMQ API完成逻辑服务的角色,但是ZeroMQ仅提供非持久化的队列,如果down机,数据将会丢失,如:Twitter的Storm中使用ZeroMQ作为数据流的传输
ZeroMQ套接字时与传输层无关的,:ZeroMQ套接字对所有传输层协议定义了统一的API接口,默认支持进程内(inproc),进程间(IPC),多播,TCP协议,子啊不同的协议之间切换只要简单的改变连接字符串的前缀,可以在任何时候以最小的代价从进程间的本地通信切换到分布式下的TCP通信,ZeroMQ在背后处理连接建立,断开和重连逻辑
特性
无锁的队列模式:对于跨线程间的交互(用户端和session)之间的数据交换通道pipe,采用无锁的队列算法CAS,在pipe的两端注册有异步事件,在读或者写消息到pipe的时候,会自动触发读写事件
批量处理的算法:对于批量的消息,进行了适应性的优化,可以批量的接受和发送消息
多核下的线程绑定,无须CPU切换:区别于传统的多线程并发模式,信号量或者临界区,zeroMQ充分利用多核的优势,每个核绑定运行一个工作者线程,避免多线程之间的CPU切换开销

比较

MQ - 图2