消息中间件是基于队列与消息传递技术,在网络环境中为应用系统提供同步或异步、可靠的消息传输的支撑性软件系统。
队列是一种线性表。它允许在表的一端插入数据,在另一端删除元素。插入元素的这一端称之为队尾。删除元素的这一端我们称之为队首。
消息是指软件对象之间进行交互作用和通讯利用的一种方式。
消息就是应用程序中间传输的数据,例如:用户下的订单、用户发表的评论、各种传感器采集的数据等等,这些都是消息,所以消息就是数据的等价名词
消息中间件是基于队列与消息传递技术,在网络环境中为应用系统提供同步或异步、可靠的消息传输的支撑性软件系统。
消息中间件关注于数据的发送和接收,利用高效可靠的异步消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
消息队列是消息中间件的一种实现方式。
大部分的消息中间件都是通过消息队列这种技术来实现的,例如:ActiveMQ、RabbitMQ 、ZeroMQ、Kafka、MetaMQ、RocketMQ等。
消息队列的特点:
先进先出:消息队列的顺序在入队的时候就基本已经确定了,一般是不需人工干预的。
发布订阅:发布订阅是一种很高效的处理方式,如果不发生阻塞,基本可以当成是同步操作。
持久化:持久化确保消息队列的使用不只是一个部分场景的辅助工具,而是让消息队列能像数据库一样存储核心的数据。
分布式:在现在大流量、大数据的使用场景下,支持分布式的部署,才能被广泛使用。消息队列的定位就是一个高性能的中间件。
系统解耦
交互系统之间没有直接的调用关系,只是通过消息传输,故系统侵入性不强,耦合度低。
提高系统响应时间
例如原来的一套逻辑,完成支付可能涉及先修改订单状态、计算会员积分、通知物流配送几个逻辑才能完成;通过MQ架构设计,就可将紧急重要(需要立刻响应)的业务放到该调用方法中,响应要求不高的使用消息队列,放到MQ队列中,供消费者处理。
为大数据处理架构提供服务
通过消息作为整合,大数据的背景下,消息队列还与实时处理架构整合,为数据处理提供性能支持。
Java消息服务——JMS
Java消息服务(Java Message Service,JMS)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
JMS中的P2P和Pub/Sub消息模式:点对点(point to point, queue)与发布订阅(publish/subscribe,topic)最初是由JMS定义的。这两种模式主要区别或解决的问题就是发送到队列的消息能否重复消费(多订阅)。
当你需要使用 消息队列 时,首先需要考虑它的必要性。可以使用消息队列的场景有很多,最常用的几种,是做 应用程序松耦合、异步处理模式、发布与订阅、最终一致性、错峰流控 和 日志缓冲 等。反之,如果需要 强一致性,关注业务逻辑的处理结果,则使用 RPC 显得更为合适。
场景说明:
提供新用户注册后,发送短信或邮件的功能
传统方式:
传统的做法有有两种 1.串行方式;2.并行方式
串行方式
将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。
并行方式
将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。
分析:
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。
因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。
消息中间件方式:
引入消息队列,将不是必须的业务逻辑,将异步处理改造后的架构如下:
分析:
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。至于从消息队列中发送邮件和短信完全可以异步处理,不受时间的限制,等邮件服务和短信服务需要的时候从消息队列中消费消息即可。
因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。
场景说明:
用户下单后,订单系统需要通知库存系统。
传统方式:
传统的做法是,订单系统直接调用库存系统的接口。
传统模式的缺点:假如库存系统无法访问,则订单减库存将失败,从而导致订单失败,订单系统与库存系统耦合
消息中间件方式
消息中间件方式:
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作
假如在下单时库存系统不能正常使用也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦
场景说明:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。通过加入消息队列完成如下功能:
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面。秒杀业务根据消息队列中的请求信息,再做后续处理。
具体场景:
用户新上传了一批照片, 人脸识别系统需要对这个用户的所有照片进行聚类,聚类完成后由对账系统重新生成用户的人脸索引(加快查询)。这三个子系统间由消息队列连接起来,前一个阶段的处理结果放入队列中,后一个阶段从队列中获取消息继续处理。
该方法有如下优点:
将消息队列用在 日志处理 中,比如 Kafka
的应用,解决 海量日志 传输和缓冲的问题。
应用案例:
把日志进行集中收集,用于计算 PV
、用户行为分析 等等。
此时需要一个集中化的日志管理系统,现在比较流行的方案是ELK(elasticsearch、logstash、kibana)+ Kafka的方案
在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。
消息队列一般都内置了 高效的通信机制,因此也可以用于单纯的 消息通讯,比如实现 点对点消息队列 或者 聊天室 等。
JMS即Java消息服务(Java message service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
优点:异步、可靠
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的功能。
当前使用较多的消息队列有RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMQ等,而部分数据库如Redis、MySQL以及phxsql也可实现消息队列的功能。
RabbitMQ于2007年发布,是一个在AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。
阿里系下开源的一款分布式、队列模型的消息中间件,原名Metaq,3.0版本名称改为RocketMQ,是阿里参照kafka设计思想使用java实现的一套mq。同时将阿里系内部多款mq产品(Notify、metaq)进行整合,只维护核心功能,去除了所有其他运行时依赖,保证核心功能最简化,在此基础上配合阿里上述其他开源产品实现不同场景下mq的架构,在阿里内部被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。
支持的客户端语言不多,目前是Java及C++,其中C++还不成熟;
ActiveMQ是由Apache出品,ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现。它非常快速,支持多种语言的客户端和协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。
Apache Kafka是LinkedIn开源的分布式发布-订阅消息系统(使用scala实现的一个高性能分布式Publish/Subscribe消息队列系统)。它最初由LinkedIn公司基于独特的设计实现为一个分布式的日志提交系统(a distributed commit log),之后成为Apache项目的一部分,目前归属于Apache顶级项目。
Kafka主要为高吞吐量的订阅发布系统而设计,追求速度与持久化。kafka中的消息由键、值、时间戳组成,kafka不记录每个消息被谁使用,只通过偏移量记录哪些消息是未读的,kafka中可以指定消费组来实现订阅发布的功能。
RabbitMQ | RocketMQ | ActiveMQ | Kafka | |
---|---|---|---|---|
设计定位 | 可靠消息传输 | 非日志的可靠消息传输 | 可靠消息传输 | 实时数据处理及日志处理 |
所属社区/公司 | Apache | Alibaba开发,现已加入Apache | Apache | Apache |
成熟度 | 成熟 | 成熟 | 成熟 | 日志领域成熟 |
社区活跃度 | 高 | 中 | 高 | 高 |
API完备性 | 高 | 高 | 高 | 高 |
开发语言 | ErLang | Java | Java | Scala |
持久化方式 | 内存、文件 | 磁盘文件 | 内存、文件、数据库 | 磁盘文件 |
客户端支持语言 | Python Java Ruby PHP C# JavaScript Go Elixir Objective-C Swift |
Java、C++(不成熟) | C C++ C# Delphi Erlang Adobe Flash Haskell Java JavaScript Perl PHP Pike Pytho Ruby |
C C++ Erlang Java .net per PHP Python Ryby Go JavaScript |
部署方式 | 单机/集群 | 单机/集群 | 单机/集群 | 单机/集群 |
集群管理方式 | 独立 | nameserver | 独立 | zookeeper |
可用性 | 高,基于主从架构实现高可用 | 非常高,分布式架构 | 高,基于主从架构实现高可用 | 非常高,分布式。一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息写入性能 | 较好 | 很好 | 较好 | 非常好 |
单机队列数 | 依赖内存 | 单机最高5万 | 较好 | 单机超过64个队列或分区时负载变高 |
单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 10 万级,高吞吐 | 万级,比 RocketMQ、Kafka 低一个数量级 | 10 万级,高吞吐 |
时效性 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | ms 级 | 延迟在 ms 级以内 |
事务消息 | 不支持 | 支持 | 支持 | 不支持 |
消息过滤 | 不支持 | 支持 | 不支持 | 不支持 |
消息失败重试 | 支持 | 支持 | 支持 | 不支持 |
消息重新消费(消息幂等性问题) | 不支持 | 支持 | 支持 | 支持 |
批量发送 | 不支持 | 支持 | 支持 | 支持 |
消息清理 | 可用内存少于40%触发gc | 指定文件保存时间过期删除 | 指定文件保存时间过期删除 | 指定文件保存时间过期删除 |
结论:
消息中间件的传输模式是指消息的发送和接受模式,大致分为两种:
点对点模型 用于 消息发送者 和 消息接收者 之间 点到点 的通信。消息发送者将消息发送到由某个名字标识的特定接收者。这个名字实际上对于消费服务中的一个 队列(Queue),在消息传递给接收者之前它被 存储 在这个队列中。队列消息 可以放在 内存 中也可以 持久化,以保证在消息服务出现故障时仍然能够传递消息。
传统的点对点消息中间件通常由 消息队列服务、消息传递服务、消息队列 和 消息应用程序接口 API 组成,其典型的结构如下图所示:
特点:
在点对点模型中,即使许多消息接收者在同一个消息队列中侦听,但是消息从消息发送者发送到只有一个接收者。
在点对点模式中又有两种类型的点对点消息传递:
在即发即弃中,消息发送方不会等待来自消息队列的任何响应。它不关心消息队列是否接收到消息,在这个模式中,消息发送者和接收者根本没有交互。
与即发即弃模式不同,在请求/回复消息模式中,消息发送方在队列上发送一条消息,然后等待接收方的响应。使用这种模式,发送关心它是否收到或未收到的消息状态。
发布者/订阅者 模型消息传递或通常称为发布/订阅消息传递时一种异步形式。
发布者/订阅者 模型支持向一个特定的 消息主题 生产消息。0 或 多个订阅者 可能对接收来自 特定消息主题 的消息感兴趣。
在这种模型下,发布者和订阅者彼此不知道对方,就好比是匿名公告板。这种模式被概况为:多个消费者可以获得消息,在 发布者 和 订阅者 之间存在 时间依赖性。发布者需要建立一个 订阅(subscription),以便能够消费者订阅。订阅者 必须保持 持续的活动状态 并 接收消息。在这种情况下,在订阅者 未连接时,发布的消息将在订阅者 重新连接 时 重新发布。
点对点和发布/订阅消息模型之间的区别在于消息接收者数量。另一个区别是在点对点模型中,消息发送者必须知道接收方,但在发布/订阅中,消息发布者不需要知道消息将在哪里消费。该特性在应用 发布/订阅消息模型时为应用程序提供了高度解耦。
特性:
注意:
- 发布者和订阅者有时间依赖:接受者和发布者只有建立订阅关系才能收到消息;
- 持久订阅:订阅关系建立后,消息就不会消失,不管订阅者是否都在线;
- 非持久订阅:订阅者为了接受消息,必须一直在线。当只有一个订阅者时约等于点对点模式