MQ(message queue)消息队列,也叫消息中间件。
消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。
它是类似于数据库一样需要独立部署在服务器上的一种应用,提供接口给其他系统调用。
消息中间件是遵守JMS(java message service)规范的一种软件(大多数消息中间件遵守JMS规范)。
要使用Java消息服务,你必须要有一个JMS提供者,管理会话和队列。现在既有开源的提供者也有专有的提供者。
开源的提供者包括:Apache ActiveMQ、Kafka、WebMethods、阿里的RocketMQ等。
点对点模式:Point-to-Point(P2P)
消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。
消息被消费以后,queue中不再存储,所以消息消费者不可能消费到已经被消费的消息。 Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
每个消息只有一个消费者。一旦被消费,消息就不再在消息队列中。
提供者和消费者之间在时间上没有依赖性。当提供者发送了消息之后,不管消费者有没有正在运行,它不会影响到消息被发送到队列。
每条消息仅会传送给一个消费者。可能会有多个消费者在一个队列中侦听,但是每个队列中的消息只能被队列中的一个消费者所消费。
消息存在先后顺序。一个队列会按照消息服务器将消息放入队列中的顺序,把它们传送给消费者。当已被消费时,就会从队列头部将它们删除(除非使用了消息优先级)。
消费者在成功接收消息之后需向队列应答成功。
queue实现了负载均衡,将producer生产的消息发送到消息队列中,由多个消费者消费。但一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存直到有一个可用的消费者。
发布订阅模式:Publish/Subscribe(Pub/Sub)
消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。
topic实现了发布和订阅,当你发布一个消息,所有订阅这个topic的服务都能得到这个消息,所以从1到N个订阅者都能得到一个消息的拷贝。
同步
订阅者或消费者调用receive方法来接收消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞。
异步
订阅者或消费者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。
ConnectionFactor接口(连接工厂)
用于创建连接到消息中间件的连接工厂。
创建Connection对象的工厂,根据消息类型的不同,用户将使用队列连接工厂QueueConnectionFactory或者主题连接工厂TopicConnectionFactory两种。可以通过JNDI来查找ConnectionFactory对象。
Connection接口(连接)
Connection表示在客户端和JMS系统之间建立的链接(对TCP/IP socket的包装),代表了应用程序和消息服务器之间的通信链路。
Connection可以产生一个或多个Session。跟ConnectionFactory一样,Connection也有两种类型:QueueConnection和TopicConnection。
Destination接口(目标)
Destination是一个包装了消息目标标识符的被管对象,消息目标是指消息发布和接收的地点,或者是队列,或者是主题。
它是消息生产者的消息发送目标或者说消息消费者的消息来源。
所以,Destination实际上就是两种类型的对象:Queue、Topic可以通过JNDI来查找Destination。
Session接口(会话)
Session是我们操作消息的接口。表示一个单线程的上下文,用于发送和接收消息。
由于会话是单线程的,所以消息是连续的,就是说消息是按照发送的顺序一个一个接收的。
可以通过session创建生产者、消费者、消息等。Session提供了事务的功能。当我们需要使用session发送/接收多个消息时,可以将这些发送/接收动作放到一个事务中。同样,也分QueueSession和TopicSession。
MessageProducer接口(消息生产者)
消息生产者由Session创建,并用于将消息发送到Destination。消费者可以同步地(阻塞模式),或异步(非阻塞)接收队列和主题类型的消息。
同样,消息生产者分两种类型:QueueSender和TopicPublisher。可以调用消息生产者的方法(send或publish方法)发送消息。
MessageConsumer接口(消息消费者)
消息消费者由Session创建,用于接收被发送到Destination的消息。两种类型:QueueReceiver和TopicSubscriber。
可分别通过session的createReceiver(Queue)或createSubscriber(Topic)来创建,也可以session的creatDurableSubscriber方法来创建持久化的订阅者。
Message接口(消息)
是在消费者和生产者之间传送的对象,也就是说从一个应用程序创送到另一个应用程序。一个消息有三个主要部分。
消息接口非常灵活,并提供了许多方式来定制消息的内容。
MessageListener(监听器)
消息监听器,如果注册了消息监听器,一旦消息到达,将自动调用监听器的onMessage方法。
EJB中的MDB(Message-Driven Bean)就是一种MessageListener。
系统之间没有直接的调用关系,只是通过消息传输,故系统侵入性不强,耦合度低。
消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
对于一些非必须及时处理的业务,通过消息队列可以优化系统响应时间。提升系统性能。
使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
分布式系统产生的海量数据流,如:业务日志、监控数据、用户行为等,针对这些数据流进行实时或批量采集汇总,然后进行大数据分析是当前互联网的必备技术,通过消息队列完成此类数据收集是最好的选择。
有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。
许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。通过消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,当应用发生变化时,可以独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。
AMQP(Advanced Message Queuing Protocol)高级消息队列协议,一个提供统一消息服务的应用层标准协议,是应用层协议的一个开放标准,为面向消息的中间件设计。
基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。
优点:可靠、通用
部分相关产品:
MQTT(Message Queuing Telemetry Transport)消息队列遥测传输,是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。
该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。
优点:格式简洁、占用带宽小、移动端通信、PUSH、嵌入式系统
STOMP(Streaming Text Orientated Message Protocol)流文本定向消息协议,是一种为MOM(Message Oriented Middleware)面向消息的中间件设计的简单文本协议。STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。
优点:命令模式(非topic\queue模式)
部分相关产品:
XMPP(Extensible Messaging and Presence Protocol)可扩展消息处理现场协议,是基于可扩展标记语言(XML)的协议,多用于即时消息(IM)以及在线现场探测。适用于服务器之间的准即时操作。
核心是基于XML流传输,这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
优点:通用公开、兼容性强、可扩展、安全性高,但XML编码格式占用带宽大
有些特殊框架(如:redis、kafka、zeroMq等)根据自身需要未严格遵循MQ规范,而是基于TCP\IP自行封装了一套协议,通过网络socket接口进行传输,实现了MQ的功能。
仅提供较少的核心功能;
提供超高的吞吐量;
ms级的延迟;
极高的可用性以及可靠性;
分布式可以任意扩展;
一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用;
topic的大幅增加会导致吞吐量的大幅度下降;
所以尽量保证topic数量不要过多,以保证其超高吞吐量。如果要支撑大规模topic,需要增加更多的机器资源
消息有可能重复消费;
天然适合大数据实时计算以及日志收集,在大数据领域中以及日志采集得以广泛使用。
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
成熟度 | 成熟 | 成熟 | 比较成熟 | 成熟日志领域 |
社区活跃度 | 中 | 高 | 较高 | 高 |
开发语言 | Java | Erlang | Java | Scala |
跨语言 | 支持,Java优先 | 语言无关 | 只支持Java | 支持,Java优先 |
支持协议 | AMQP、MQTT、STOMP、OpenWire | AMQP、MQTT、STOMP | MQTT、TCP | Kafka |
JMS规范 | 支持 | 支持 | 支持得不够好 | 不支持 |
持久化 | 内存、文件、数据库 | 内存、文件 | 磁盘文件 | 磁盘文件 |
可用性 | 高(主从) | 高(主从) | 非常高(分布式) | 非常高(分布式) |
单机吞吐量 | 万级 | 万级 | 万级 | 十万级 |
消息延迟 | 毫秒级 | 微秒级 | 毫秒级 | 毫秒级 |
可靠性 | 有较低的概率丢失数据 | 有较低的概率丢失数据 | 经过参数优化配置,可以做到0丢失 | 经过参数优化配置,消息可以做到0丢失 |
事务 | 支持 | 支持 | 支持 | 支持 |
集群 | 支持 | 支持 | 支持 | 支持 |
负载均衡 | 支持 | 支持 | 支持 | 支持 |
文档 | 完备 | 完备 | 完备 | 完备 |
是否开源 | 开源 | 开源 | 开源 | 开源 |
所属社区/公司 | Apache | Rabbit | Apache | Apache |
消息服务默认端口 | 61616 | 5672 | 10911 | 9092 |
管理后台 | 有 | 有 | 单独部署 | 无 |
管理后台默认端口 | 8161 | 15672 | 8080 | - |
部署方式 | 独立、嵌入 | 独立 | 独立 | 独立 |
评价 | 产品成熟,功能齐全,大量公司使用;有较低概率丢失消息;社区不够活跃,版本维护较少,公司产品重心不在该产品上 | Erlang开发,性能好,延迟低;大量公司使用;社区比较活跃;但erlang语言难度大,集群动态扩容很麻烦 | 功能较为完善,社区比较活跃;还是分布式的,扩展性好 | 功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用 |
消息分发策略对比:
消息分发策略 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
发布订阅 | 支持 | 支持 | 支持 | 支持 |
轮询分发 | 支持 | 支持 | - | 支持 |
公平分发 | - | 支持 | - | 支持 |
重发 | 支持 | 支持 | 支持 | - |
消息拉取 | - | 支持 | 支持 | 支持 |
最早大家用ActiveMQ。但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃。
后来大家用RabbitMQ。但是确实erlang语言阻止了大量的java工程师去深入研究和掌控他,对公司而言,几乎处于不可控的状态,但是确实人是开源的,比较稳定的支持,活跃度也高。
现在确实越来越多的公司会去用RocketMQ。