文章目录
前言
消息队列
以12306为例,假设平时可能买票的人不多,所以订单系统的QPS( 每秒查询率 )也不是很高,每秒也就处理1000个请求,但是一到节假日、春运期间可能抢票的人就非常多,并发量远远大于平时。这个时候,订单系统明显扛不住了,怎么办呢?当然我们可以设计弹性伸缩的集群,进行机器扩容,保证高可用。但是我们依然可以采用MQ来解决这个问题。
一、MQ介绍
1. 什么是MQ
MQ(Message Queue)是一种跨进程的通信机制,用于上下游传递消息。本质是个 FIFO 先进先出的数据结构,只不过队列中存放的内容是 message 。
2. MQ的应用场景
- 异步解耦
最常见的一个场景是用户注册后,需要发送注册邮件和短信通知,以告知用户注册成功。传统的做法如下:
此架构下注册、邮件、短信三个任务全部完成后,才返回注册结果到客户端,用户才能使用账号登录。但是对于用户来说,注册功能实际只需要注册系统存储用户的账户信息后,该用户便可以登录,而后续的注册短信和邮件不是即时需要关注的步骤。
所以实际当数据写入注册系统后,注册系统就可以把其他的操作放入对应的消息队列 MQ 中然后马上返回用户结果,由消息队列 MQ 异步地进行这些操作。架构图如下:
异步解耦是消息队列 MQ 的主要特点,主要目的是减少请求响应时间和解耦。主要的使用场景就是将比较耗时而且不需要即时(同步)返回结果的操作作为消息放入消息队列。同时,由于使用了消息队列MQ,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦合。
- 流量削峰
流量削峰也是消息队列 MQ 的常用场景,一般在秒杀或团队抢购(高并发)活动中使用广泛。
在秒杀或团队抢购活动中,由于用户请求量较大,导致流量暴增,秒杀的应用在处理如此大量的访问流量后,下游的通知系统无法承载海量的调用量,甚至会导致系统崩溃等问题而发生漏通知的情况。为解决这些问题,可在应用和下游通知系统之间加入消息队列 MQ。
秒杀处理流程如下所述:
- 用户发起海量秒杀请求到秒杀业务处理系统。
- 秒杀处理系统按照秒杀处理逻辑将满足秒杀条件的请求发送至消息队列 MQ。
- 下游的通知系统订阅消息队列 MQ 的秒杀相关消息,再将秒杀成功的消息发送到相应用户。
- 用户收到秒杀成功的通知。
3. 常见的MQ产品
目前业界有很多MQ产品,比较出名的有下面这些:
-
ActiveMQ
历史悠久的Apache开源项目。已经在很多产品中得到应用,实现了JMS1.1规范,可以和springjms轻松融合,实现了多种协议,支持持久化到数据库,对队列数较多的情况支持不好。 -
RabbitMQ
使用erlang语言开发,性能较好,适合于企业级的开发。但是不利于做二次开发和维护。 -
RocketMQ
阿里巴巴的MQ中间件,由java语言开发,性能非常好,能够撑住双十一的大流量,而且使用起来很简单。 -
Kafka
Kafka是Apache下的一个子项目,是一个高性能跨语言分布式Publish/Subscribe消息队列系统,相对于ActiveMQ是一个非常轻量级的消息系统,除了性能非常好之外,还是一个工作良好的分布式系统。 -
ZeroMQ
号称最快的消息队列系统,尤其针对大吞吐量的需求场景。扩展性好,开发比较灵活,采用C语言实现,实际上只是一个socket库的重新封装,如果做为消息队列使用,需要开发大量的代码。
ZeroMQ仅提供非持久性的队列,也就是说如果down机,数据将会丢失。
4. MQ选型总结
需要根据具体的应用场景和需求的多维度来决定:
性能:吞吐量、并发时效性
可靠性:可用性、集群支持、持久化、性能稳定性、安全性
可维护性:管理界面等运维能力
易用性:平台熟悉度
兼容可扩展性:AMQP支持
服务支持:社区活跃度
-
ActiveMQ
作为老牌的消息队列,吞吐量比较低,也缺少大规模吞吐量场景的验证、社区活跃度也很低,数据持久化的支持一般,目前渐渐被淘汰,已经不是主流了,不太建议选择了。 -
RabbitMQ和RocketMQ
社区比较活跃,吞吐量比较高,支持AMQP,稳定性也比较好,如果你的场景是应用需要可靠性消息传递和较高的并发,那么这两者是比较好的选择。
要注意,rabbitMQ是使用Erlang语言开发的,而RocketMQ则使用Java语言开发,所以如果是需要深度研究掌握的话,要考虑团队中是否有Erlang工程师,如果不具备相关的人才储备的话,更建议选择RocketMQ。当然,如果只是小团队简单使用,则rabbitMQ是一个挺好的选择。 -
Kafka和Pulsar
如果是大数据领域的实时计算、日志采集等场景,那么这两者是比较好的选择。
Kafka经历了超大规模应用的验证,社区活跃度很高,性能也非常高,几乎是全世界这个领域的事实性的标准。
Pulsar作为新兴的分布式消息传递系统,可扩展性强、性能高、社区活跃度也很高,最重要的是支持存储和计算分离,这在云原生下是非常出色的一项能力,并且天然支持跨数据中心的容灾,目前的应用也越来越广泛,如果集群对于持久化要求高,数据级别是超大规模,对于机器成本敏感,且支持多数据中心容灾,则建议选择Pulsar。 -
RabbitMQ:消息队列并不是构建系统的关键中间件,对消息队列功能和性能都没有很高的要求,只需要一个开箱即用并且易于维护的产品,建议使用 RabbitMQ。
-
RocketMQ:系统使用消息队列主要场景是处理在线业务,比如在交易系统中用消息队列传递订单,那 RocketMQ 的低延迟和金融级的稳定性正是系统需要的。
-
Kafka:需要处理海量消息,像收集日志、监控信息或是前端的埋点这类数据,或是应用场景大量使用了大数据、流计算相关的开源产品,那 Kafka 是最适合的消息队列。
比较项 | ActiveMQ | RabbitMQ | RocketMQ | Kafka | Pulsar |
---|---|---|---|---|---|
单机吞吐量 | 较低(万级) | 一般(万级) | 高(十万级) | 高(十万级) | 高(十万级) |
时效性 | ms级 | us级 | ms级 | ms级以内 | - |
可用性 | 高(主从架构) | 高(主从架构) | 非常高(分布式架构) | 非常高(分布式架构) | - |
持久化 | 支持(小) | 支持(小) | 支持(大) | 支持(大) | 支持(大) |
顺序消息 | 不支持 | 不支持 | 支持 | 支持 | 支持 |
性能稳定性 | 好 | 好 | 一般 | 较差 | 一般 |
集群支持 | 主备模式 | 镜像模式(复制) | 主备模式 | Leader-Slave每台既是master也是slave,集群可扩展性强 | 集群模式,broker无状态,易迁移,支持跨数据中心 |
消费模式 | P2P、Pub-Sub | direct、fanout、topic、Headers | 基于Topic和MessageTag的的Pub-Sub | 基于Topic的Pub-Sub | 基于Topic的Pub-Sub,支持独占(exclusive)、共享(shared)、灾备(failover)、key共享(key_shared)4种模式 |
管理界面 | 一般 | 较好 | 一般 | 无 | 无 |
计算和存储分离 | 不支持 | 不支持 | 不支持 | 不支持 | 支持 |
AMQP支持 | 支持 | 支持 | 支持 | 不完全支持 | 不完全支持 |
开发语言 | Java | Erlang | Java | Java/Scala | Java |
维护者 | Apache | Spring | Apache(Alibaba) | Apache(Confluent) | Apache(StreamNative) |
Star数量 | 2.1K | 10.4K | 18.8K | 24.3K | 12.4K |
Contributor | 126 | 246 | 438 | 991 | 600 |
社区活跃度 | 低 | 高 | 较高 | 高 | 高 |
功能特性 | 成熟的产品,有较多的文档;各种协议支持较好 | 并发能力很强,性能很好,延时很低;管理界面丰富 | MQ功能比较完毕,扩展性佳 | 支持主要的MQ功能,在大数据领域应用广泛 | - |
二、RocketMQ入门
RocketMQ是阿里巴巴开源的分布式消息中间件,现在是Apache的一个顶级项目。在阿里内部使用非常广泛,已经经过了"双11"这种万亿级的消息流转。
1. RocketMQ环境搭建
接下来我们先在linux平台下安装一个RocketMQ的服务
- 环境准备
下载地址:RocketMQ
环境要求
- Linux 64位操作系统
- 64bit JDK 1.8+
- 安装RocketMQ
1 上传文件到Linux系统
[root@rocketmq]# ls /usr/local/src/
rocketmq-all-4.4.0-bin-release.zip
2 解压到安装目录
[root@src]# unzip rocketmq-all-4.4.0-bin-release.zip
[root@src]# mv rocketmq-all-4.4.0-bin-release ../rocketmq
- 启动RocketMQ
1 切换到安装目录
[root@rocketmq]# ls
benchmark bin conf lib LICENSE NOTICE README.md
2 启动NameServer
[root@rocketmq]# nohup ./bin/mqnamesrv &
[1] 1467
# 只要进程不报错,就是启动成功了,可以查看一下日志
[root@rocketmq]# tail -f /root/logs/rocketmqlogs/namesrv.log
3 启动Broker
# 编辑bin/runbroker.sh 和 bin/runserver.sh文件,修改里面的
# JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g -Xmn4g"
# 为JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m"
[root@rocketmq]# nohup bin/mqbroker -n localhost:9876 &
[root@rocketmq]# tail -f /root/logs/rocketmqlogs/broker.log
- 测试RocketMQ
1 测试消息发送
[root@rocketmq]# export NAMESRV_ADDR=localhost:9876
[root@rocketmq]# bin/tools.sh
org.apache.rocketmq.example.quickstart.Producer
2 测试消息接收
[root@rocketmq]# export NAMESRV_ADDR=localhost:9876
[root@rocketmq]# bin/tools.sh
org.apache.rocketmq.example.quickstart.Consumer
- 关闭RocketMQ
[root@rocketmq]# bin/mqshutdown broker
[root@rocketmq]# bin/mqshutdown namesr
2. RocketMQ的架构及概念
如上图所示,整体可以分成4个角色,分别是:NameServer,Broker,Producer,Consumer。
-
Broker(邮递员)
Broker是RocketMQ的核心,负责消息的接收,存储,投递等功能 -
NameServer(邮局)
消息队列的协调者,Broker向它注册路由信息,同时Producer和Consumer向其获取路由信息 -
Producer(寄件人)
消息的生产者,需要从NameServer获取Broker信息,然后与Broker建立连接,向Broker发送消息 -
Consumer(收件人)
消息的消费者,需要从NameServer获取Broker信息,然后与Broker建立连接,从Broker获取消息 -
Topic(地区)
用来区分不同类型的消息,发送和接收消息前都需要先创建Topic,针对Topic来发送和接收消息 -
Message Queue(邮件)
为了提高性能和吞吐量,引入了Message Queue,一个Topic可以设置一个或多个MessageQueue,这样消息就可以并行往各个Message Queue发送消息,消费者也可以并行的从多个
Message Queue读取消息 -
Message
Message 是消息的载体。 -
Producer Group
生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。 -
Consumer Group
消费者组,消费同一类消息的多个 consumer 实例组成一个消费者组。
3. RocketMQ控制台安装
1 下载
# 在git上下载下面的工程 rocketmq-console-1.0.0
https://github.com/apache/rocketmq-externals/releases
2 修改配置文件
# 修改配置文件 rocketmq-console\src\main\resources\application.properties
server.port=7777 #项目启动后的端口号
rocketmq.config.namesrvAddr=192.168.109.131:9876 #namesrv的地址,注意防火墙要开启 9876 端口
3 打成jar包,并启动
# 进入控制台项目,将工程打成jar包
mvn clean package -Dmaven.test.skip=true
# 启动控制台
java -jar target/rocketmq-console-ng-1.0.0.jar
4 访问控制台
三、RocketMQ消息发送和接收演示
接下来我们使用Java代码来演示消息的发送和接收
1. 添加依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.2