分类:MQ
跳到导航
跳到搜索
消息中间件
关于
系统间通信方式?
一种是基于远程过程调用的方式(如 RPC 调用);另一种是基于消息队列的方式。
什么是MQ?
消息队列(英语:Message queue)是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户。消息队列提供了异步的通信协议,每一个贮列中的纪录包含详细说明的数据,包含发生的时间,输入设备的种类,以及特定的输入参数,也就是说:消息的发送者和接收者不需要同时与消息队列互交。消息会保存在队列中,直到接收者取回它。 ——维基百科
MQ(Message Queue)是一种异步的服务间通信方式,适用于无服务器和微服务架构。消息在被处理和删除之前一直存储在队列上。
- 通俗点说,就是一个先进先出的数据结构。
为什么使用MQ?(作用)
消息队列的作用有很多,但是比较核心的有 3 个:
- 解耦(应用解耦):一个业务需要多个模块共同实现,或者一条消息有多个系统需要对应处理,只需要主业务完成以后,发送一条MQ,其余模块消费MQ消息,即可实现业务,降低模块之间的耦合。
- 异步(异步消息):主业务执行结束后从属业务通过MQ,异步执行,减低业务的响应时间,提高用户体验。
- 削峰(流量削锋):高并发情况下,业务异步处理,提供高峰期业务处理能力,避免系统瘫痪。
MQ有哪些缺点?
- 系统可用性降低:系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套系统崩溃的,你不就完了?
- 系统复杂度提高:硬生生加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。
- 一致性问题:A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。
MQ有哪些使用场景?
常用的使用场景:
- 异步通信:有些业务不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
- 解耦:降低工程间的强依赖程度,针对异构系统进行适配。在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。通过消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,当应用发生变化时,可以独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
- 冗余:有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的”插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
- 扩展性:因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、不需要调节参数。便于分布式扩容。
- 过载保护:在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量无法提取预知;如果以为了能处理这类瞬间峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
- 可恢复性:系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
- 顺序保证:在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。
- 缓冲:在任何重要的系统中,都会有需要不同的处理时间的元素。消息队列通过一个缓冲层来帮助任务最高效率的执行,该缓冲有助于控制和优化数据流经过系统的速度。以调节系统响应时间。
- 数据流处理:分布式系统产生的海量数据流,如:业务日志、监控数据、用户行为等,针对这些数据流进行实时或批量采集汇总,然后进行大数据分析是当前互联网的必备技术,通过消息队列完成此类数据收集是最好的选择。
MQ 基础
消息中间件模式分类
- 点对点(PTP):使用 queue 作为通信载体。
- 消息被消费以后,queue中不再存储,所以消息消费者不可能消费到已经被消费的消息。 【不存在重复消费】
- Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。【一个消息对应一个消费者】
- 发布/订阅(Pub/Sub):使用 topic 作为通信载体
- 和点对点方式不同,发布到topic的消息会被所有订阅者消费。【一个消息对应多个消费者】
消息推拉模式
- pull:(拉)主动权在于消费方,优点是按需消费(吃自助餐,能吃多少拿多少),而且服务端队列堆积的消息处理也相对简单。
- 缺点就是消息延迟(不知道啥时候去拉取更新)。
- push:(推)主动权就在服务方了,优点是实时性高,服务端可以统一管理来进行负载,不过也容易导致慢消费。
- 缺点就是发送消息的状态是集中式管理。
当然也可以根据实际情况,将二者结合使用来:1、提高消息的实时性;2、按需消费;
消息中间件常用协议
- AMQP 协议:
- AMQP 即 Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。
- 优点:可靠、通用
- MQTT 协议
- MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是 IBM 开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。
- 优点:格式简洁、占用带宽小、移动端通信、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需要考虑的问题
怎么保证消息没有重复消费?
- 如果是拿这个消息做数据库 insert 操作(事实上 update 和 delete 重复也不影响)给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
- 当拿到这个消息做 redis 的 set 的操作,那就容易了,不用解决,因为你无论 set 几次结果都是一样的,set 操作本来就算幂等操作。
- 如果上面两种情况还不行,准备一个第三方存储,来做消费记录。以 redis 为例,给消息分配一个全局 id,只要消费过该消息,将 <id,message> 以 K-V 形式写入 redis。那消费者开始消费前,先去 redis 中查询有没消费记录即可。
怎么处理消息丢失的情况?
怎么保证消息传递的顺序性?
怎么保证多系统消息一致性?
选型
总的来说:
- 中小型软件公司,建议选 RabbitMQ;
- 大型软件公司,根据具体使用在 RocketMQ 和 Kafka 之间二选一。
- 根据业务场景选择,如果有大数据、日志采集功能,首选 Kafka。
有哪些MQ?
- ActiveMQ:是 Apache 出品的、采用 Java 语言编写的完全基于JMS1.1规范的面向消息的中间件,为应用程序提供高效的、可扩展的、稳定的和安全的企业级消息通信。
- 不过由于历史原因包袱太重,目前市场份额没有后面三种消息中间件多,其最新架构被命名为 Apollo,号称下一代 ActiveMQ,有兴趣的同学可行了解。
- RabbitMQ:是采用 Erlang 语言实现的 AMQP 协议的消息中间件,最初起源于金融系统,用于在分布式系统中存储转发消息。
- RabbitMQ 发展到今天,被越来越多的人认可,这和它在可靠性、可用性、扩展性、功能丰富等方面的卓越表现是分不开的。
- Kafka:起初是由 LinkedIn 公司采用 Scala 语言开发的一个分布式、多分区、多副本且基于 Zookeeper 协调的分布式消息系统,现已捐献给 Apache 基金会。
- 它是一种高吞吐量的分布式发布订阅消息系统,以可水平扩展和高吞吐率而被广泛使用。
- 目前越来越多的开源分布式处理系统,如:Cloudera、Apache Storm、Spark、Flink 等都支持与 Kafka 集成。
- RocketMQ:是阿里开源的消息中间件,目前已经捐献个 Apache 基金会,它是由 Java 语言开发的,具备高吞吐量、高可用性、适合大规模分布式系统应用等特点。
- 经历过双11的洗礼,实力不容小觑。
- ZeroMQ:号称史上最快的消息队列,基于 C 语言开发。
- ZeroMQ是一个消息处理队列库,可在多线程、多内核和主机之间弹性伸缩,虽然大多数时候我们习惯将其归入消息队列家族之中,但是其和前面的几款有着本质的区别,ZeroMQ本身就不是一个消息队列服务器,更像是一组底层网络通讯库,对原有的Socket API上加上一层封装而已。
不同MQ的特点对比?
没有最好的消息中间件,只有最合适的消息中间件:
- 性能小、量小,用什么都没有关系,性质是一样的,如果消息性能要求高 用 RocketMQ 与 Kafka 可以更优,二者各有利弊,看业务需要。
- ActiveMQ、RabbitMQ 与 Kafka、RocketMQ 有很大的区别就是前2个只支持主从模式,后2个是分布式消息系统,支持分布式。
Kafka 与 RocketMQ
官方提供了一些不同于kafka的对比差异:
ActiveMQ | Kafka | RocketMQ | |
---|---|---|---|
Client SDK | Java, .NET, C++ etc. | Java, Scala etc. | Java, C++, Go |
协议和规范 | 推送模型,支持OpenWire、STOMP、AMQP、MQTT、JMS | 拉模型,TCP协议支持 | 拉模型,支持TCP、JMS、OpenMessage |
消息有序 | 独占消费者或独占队列可以确保有序 | 确保分区内消息的顺序 | 确保消息严格有序,并可以正常扩展 |
定时消息 | 支持 | 不支持 | 支持 |
批量消息 | 不支持 | 支持,具有异步生产者(producer) | 支持,具有同步模式,可避免消息丢失 |
广播信息 | 支持 | 不支持 | 支持 |
消息过滤器 | 支持 | 支持,您可以使用Kafka流(Streams)来过滤消息 | 支持,基于SQL92的属性过滤器表达式 |
服务器触发的重新交付 | 不支持 | 不支持 | 支持 |
消息存储 | 使用 JDBC 以及高性能日志(例如levelDB,kahaDB),支持非常快速的持久性 | 高性能的文件存储 | 高性能和低延迟的文件存储 |
消息追溯 | 支持 | 支持偏移追溯 | 支持的时间戳和偏移量追溯 |
消息优先级 | 支持 | 不支持 | 不支持 |
高可用性和故障转移 | 支持,取决于存储,如果使用levelDB,则需要ZooKeeper服务器 | 支持,需要ZooKeeper服务器 | 支持,主从模式,无其他套件 |
消息跟踪 | 不支持 | 不支持 | 支持 |
配置 | 默认配置为低级别,用户需要优化配置参数 | Kafka使用键值对(key-value)格式进行配置。这些值可以通过文件或编程方式提供。 | 开箱即用,用户只需注意几个配置 |
管理和操作工具 | 支持 | 支持,使用终端命令显示核心指标 | 支持丰富的web和终端命令以显示核心指标 |
RocketMQ 具有以下特点:
- 能够保证严格的消息顺序
- 提供针对消息的过滤功能
- 提供丰富的消息拉取模式
- 高效的订阅者水平扩展能力
- 实时的消息订阅机制
- 亿级消息堆积能力
缺点:
- 社区活跃度一般
- 没有在 mq 核心中去实现JMS等接口,有些系统要迁移需要修改大量代码
Kafka 具有以下特点:
- 快速持久化:通过磁盘顺序读写与零拷贝机制,可以在O(1)的系统开销下进行消息持久化;
- 高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
- 高堆积:支持topic下消费者较长时间离线,消息堆积量大;
- 完全的分布式系统:Broker、Producer、Consumer都原生自动支持分布式,依赖zookeeper自动实现复杂均衡;
- 支持Hadoop数据并行加载:对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。
缺点:
- Kafka单机超过64个队列/分区,Load会发生明显的飙高现象,队列越多,load越高,发送消息响应时间变长
- 使用短轮询方式,实时性取决于轮询间隔时间;
- 消费失败不支持重试;
- 支持消息顺序,但是一台代理宕机后,就会产生消息乱序;