消息中间件(消息队列)


简介

MQ(message queue)消息队列,也叫消息中间件。

消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。

它是类似于数据库一样需要独立部署在服务器上的一种应用,提供接口给其他系统调用。

JMS规范

消息中间件是遵守JMS(java message service)规范的一种软件(大多数消息中间件遵守JMS规范)。

要使用Java消息服务,你必须要有一个JMS提供者,管理会话和队列。现在既有开源的提供者也有专有的提供者。
开源的提供者包括:Apache ActiveMQ、Kafka、WebMethods、阿里的RocketMQ等。

专业术语

  • 提供者:实现JMS规范的中间件服务器。
  • 客户端:发送或者接受消息的应用程序。
  • 生产者:创建并发送消息的客户端。
  • 消费者:接受并处理消息的客户端。
  • 消息:应用程序之间传递的内容。
  • 队列:一个容纳那些被发送的等待阅读的消息的区域,一旦消息被消费,将被从队列中移走。
  • 主题 :一种支持发送消息给多个订阅者的机制。
  • 消息模式:在客户端之间传递消息的方式,JSM中定义了点对点模式(发送者接收者)和发布订阅模式(发布者订阅者)。

消息模式

点对点模式:Point-to-Point(P2P)

消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。

消息被消费以后,queue中不再存储,所以消息消费者不可能消费到已经被消费的消息。 Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
在这里插入图片描述

  1. 每个消息只有一个消费者。一旦被消费,消息就不再在消息队列中。

  2. 提供者和消费者之间在时间上没有依赖性。当提供者发送了消息之后,不管消费者有没有正在运行,它不会影响到消息被发送到队列。

  3. 每条消息仅会传送给一个消费者。可能会有多个消费者在一个队列中侦听,但是每个队列中的消息只能被队列中的一个消费者所消费。

  4. 消息存在先后顺序。一个队列会按照消息服务器将消息放入队列中的顺序,把它们传送给消费者。当已被消费时,就会从队列头部将它们删除(除非使用了消息优先级)。

  5. 消费者在成功接收消息之后需向队列应答成功。

queue实现了负载均衡,将producer生产的消息发送到消息队列中,由多个消费者消费。但一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存直到有一个可用的消费者。

发布订阅模式:Publish/Subscribe(Pub/Sub)

消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。
在这里插入图片描述

  1. 每个消息可以有多个消费者。
  2. 发布者和订阅者之间有时间上的依赖性。针对某个主题的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。
  3. 为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。
  4. 每条消息都会传送给称为订阅者的多个消息消费者。订阅者有许多类型,包括持久型、非持久型和动态型。
  5. 发布者通常不会知道哪一个订阅者正在接收主题消息。
  6. 消息被推送给消费者。这意味着消息会传送给消费者,而无须请求。

topic实现了发布和订阅,当你发布一个消息,所有订阅这个topic的服务都能得到这个消息,所以从1到N个订阅者都能得到一个消息的拷贝。

消息消费方式

  1. 同步

    订阅者或消费者调用receive方法来接收消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞。

  2. 异步
    订阅者或消费者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。

JMS规范接口

  • ConnectionFactor接口(连接工厂)
    用于创建连接到消息中间件的连接工厂。

    创建Connection对象的工厂,根据消息类型的不同,用户将使用队列连接工厂QueueConnectionFactory或者主题连接工厂TopicConnectionFactory两种。可以通过JNDI来查找ConnectionFactory对象。

  • Connection接口(连接)
    Connection表示在客户端和JMS系统之间建立的链接(对TCP/IP socket的包装),代表了应用程序和消息服务器之间的通信链路。

    Connection可以产生一个或多个Session。跟ConnectionFactory一样,Connection也有两种类型:QueueConnection和TopicConnection。

  • Destination接口(目标)
    Destination是一个包装了消息目标标识符的被管对象,消息目标是指消息发布和接收的地点,或者是队列,或者是主题。

    它是消息生产者的消息发送目标或者说消息消费者的消息来源。

    • 对于消息生产者来说,它的Destination是某个队列(Queue)或某个主题(Topic);
    • 对于消息消费者来说,它的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接口(消息)
    是在消费者和生产者之间传送的对象,也就是说从一个应用程序创送到另一个应用程序。一个消息有三个主要部分。

    1. 消息头(必须):包含用于识别和为消息寻找路由的操作设置。
    2. 一组消息属性(可选):包含额外的属性,支持其他提供者和用户的兼容。可以创建定制的字段和过滤器(消息选择器)。
    3. 一个消息体(可选):允许用户创建五种类型的消息(文本消息,映射消息,字节消息,流消息和对象消息)。消息接口非常灵活,并提供了许多方式来定制消息的内容。

    消息接口非常灵活,并提供了许多方式来定制消息的内容。

  • MessageListener(监听器)
    消息监听器,如果注册了消息监听器,一旦消息到达,将自动调用监听器的onMessage方法。

    EJB中的MDB(Message-Driven Bean)就是一种MessageListener。

消息中间件作用

1.系统解耦

系统之间没有直接的调用关系,只是通过消息传输,故系统侵入性不强,耦合度低。

2.异步通信

消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

对于一些非必须及时处理的业务,通过消息队列可以优化系统响应时间。提升系统性能。

3.流量削峰

使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。

4.数据采集

分布式系统产生的海量数据流,如:业务日志、监控数据、用户行为等,针对这些数据流进行实时或批量采集汇总,然后进行大数据分析是当前互联网的必备技术,通过消息队列完成此类数据收集是最好的选择。

5.可恢复性

有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。

许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。

6.可扩展性

在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。通过消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,当应用发生变化时,可以独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。

7.顺序保证

在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。

消息中间件协议

1.AMQP协议

AMQP(Advanced Message Queuing Protocol)高级消息队列协议,一个提供统一消息服务的应用层标准协议,是应用层协议的一个开放标准,为面向消息的中间件设计。

基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。

优点:可靠、通用

部分相关产品:

  • RabbitMQ
    一个独立的开源实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。RabbitMQ发布在Ubuntu、FreeBSD平台。
  • OpenAMQ
    AMQP的开源实现,用C语言编写,运行于Linux、AIX、Solaris、Windows、OpenVMS。
  • Apache Qpid
    Apache的开源项目,支持C++、Ruby、Java、JMS、Python和.NET。
  • Zyre
    一个Broker,实现了RestMS协议和AMQP协议,提供了RESTful HTTP访问网络AMQP的能力。

2.MQTT协议

MQTT(Message Queuing Telemetry Transport)消息队列遥测传输,是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。

该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。

优点:格式简洁、占用带宽小、移动端通信、PUSH、嵌入式系统

3.STOMP协议

STOMP(Streaming Text Orientated Message Protocol)流文本定向消息协议,是一种为MOM(Message Oriented Middleware)面向消息的中间件设计的简单文本协议。STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。

优点:命令模式(非topicqueue模式)

部分相关产品:

  • ActiveMQ

4.XMPP协议

XMPP(Extensible Messaging and Presence Protocol)可扩展消息处理现场协议,是基于可扩展标记语言(XML)的协议,多用于即时消息(IM)以及在线现场探测。适用于服务器之间的准即时操作。

核心是基于XML流传输,这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。

优点:通用公开、兼容性强、可扩展、安全性高,但XML编码格式占用带宽大

5.基于TCP/IP自定义协议

有些特殊框架(如:redis、kafka、zeroMq等)根据自身需要未严格遵循MQ规范,而是基于TCPIP自行封装了一套协议,通过网络socket接口进行传输,实现了MQ的功能。

主流消息中间件

ActiveMQ

  • 非常成熟,功能比较完备,大量公司使用;
  • 社区越来越不活跃,维护越来越少,几个月才发一次版;
  • 偶尔会有较低概率丢失消息;
  • 多数使用目的主要是用于解耦和异步通信,较少在大规模吞吐的场景中使用。

RabbitMQ

  • 比较成熟,功能比较完备,大量公司使用;
  • Erlang语言开发,性能极其好,延时很低;
  • 比较好用,社区活跃,几乎每个月都发布几个版本;
  • 吞吐量万级,和其他相比会略低一些,这是因为他做的实现机制比较重;
  • Erlang开发,语言难度大,很难读源码,很难定制和掌控。基本只能依赖于开源社区的快速维护和修复bug。
  • 集群动态扩展会很麻烦,这主要是erlang语言本身带来的问题。

RocketMQ

  • 文档相对来说简单一些,接口简单易用(接口不是按照标准JMS规范);
  • 阿里大规模应用,有保障(阿里日处理消息上百亿之多),可以做到大规模吞吐,性能也非常好;
  • 分布式扩展也很方便;
  • 社区比较活跃,维护还可以;
  • 可靠性和可用性都不错;
  • 支撑大规模的topic数量;
  • 支持复杂MQ业务场景;
  • Java语言编写,我们可以自己阅读源码。

Kafka

  • 仅提供较少的核心功能;

  • 提供超高的吞吐量;

  • ms级的延迟;

  • 极高的可用性以及可靠性;

  • 分布式可以任意扩展;

  • 一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用;

  • topic的大幅增加会导致吞吐量的大幅度下降;

    所以尽量保证topic数量不要过多,以保证其超高吞吐量。如果要支撑大规模topic,需要增加更多的机器资源

  • 消息有可能重复消费;

  • 天然适合大数据实时计算以及日志收集,在大数据领域中以及日志采集得以广泛使用。

4种MQ对比

特性 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
发布订阅 支持 支持 支持 支持
轮询分发 支持 支持 - 支持
公平分发 - 支持 - 支持
重发 支持 支持 支持 -
消息拉取 - 支持 支持 支持

MQ的选择

最早大家用ActiveMQ。但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃。

后来大家用RabbitMQ。但是确实erlang语言阻止了大量的java工程师去深入研究和掌控他,对公司而言,几乎处于不可控的状态,但是确实人是开源的,比较稳定的支持,活跃度也高。

现在确实越来越多的公司会去用RocketMQ。

  • 对于中小型公司,技术实力较为一般,技术挑战不是特别高,用RabbitMQ是不错的选择;
  • 对于大型公司,基础架构研发实力较强,用RocketMQ是很好的选择;
  • 大数据领域的实时计算、日志采集等场景,用Kafka是业内标准的,绝对没问题。社区活跃度很高,何况Kafka几乎是全世界这个领域的规范制定者。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>