RocketMq自理笔记
什么是MQ
消息:是MQ中最小的概念,本质就是一段数据。
队列:在MQ中使用队列的数据结构来存储消息。
MQ是把消息和队列结合起来,称为消息队列(Message Queue),是基础数据结构中“先进先出”的一种数据结构。指把要传输的数据(消息)放在队列中,用队列机制来实现消息传递——生产者产生消息并把消息放入队列,然后由消费者去处理。消费者可以到指定队列拉取消息,或者订阅相应的队列,由MQ服务端给其推送消息。
MQ的应用场景
应用解耦
系统的耦合性越高,容错性就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。
流量削峰:
异步解耦方式:
各大MQ产品比较
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
producer-consumer | 支持 | 支持 | 支持 | 支持 |
发布-订阅 | 支持 | 支持 | 支持 | 支持 |
API完备性 | 高 | 高 | 高 | 高 |
多语言支持 | 支持,Java优先 | 语言无关 | 只支持Java | 支持,Java优先 |
单机吞吐量 | 万级 | 万级 | 万级 | 十万级 |
消息延迟 | 微秒级 | 毫秒级 | 毫秒级 | |
可用性 | 高(主从) | 高(主从) | 非常高(分布式) | 非常高(分布式) |
消息丢失 | 低 | 低 | 理论上不会丢失 | 理论上不会丢失 |
消息重复 | 可控制 | 理论上会有重复 | ||
文档完备性 | 高 | 高 | 高 | 高 |
提供快速入门 | 有 | 有 | 有 | 有 |
部署难度 | 低 | 低 | 中 | |
社区活跃度 | 高 | 高 | 中 | 高 |
商业支持 | 无 | 无 | 阿里云 | 无 |
成熟度 | 成熟 | 成熟 | 成熟 | 成熟(日志领域) |
特点 | 功能齐全,大量项目使用 | 借助于erlang语言并发能力,性能高 | 各环节分布式设计,主从HA,支持上万队列,多种消费模式,性能好 | |
支行协议 | openwire,stomp,rest,xmpp,amqp | amqp | 自定义的一套(社区提供JMS--不成熟) | |
持久化 | 内存,文件,数据库 | 内存,文件 | 磁盘文件 | |
事务 | 支持 | 支持 | 支持 | |
负载均衡 | 支持 | 支持 | 支持 | |
管理界面 | 一般 | 好 | 有web console实现 | |
部署方式 | 独立,嵌入 | 独立 | 独立 | |
评价 | 优点:成熟的产品,已经在很多公司应用但规模不大,各种协议支持较好,有多重语言的客户端;缺点:其重点已放到activemq6.0产品appollo上去了,目前社区不活跃,且对5.x的维护较少;不适用于上千队列场景 | 优点:由于erlang语言的特点,产品性能较好,在互联网公司有较大规模应用,支持amqp协议,有多语言且支持amqp的客户端可用;缺点:erlang语言难度大,集群不支持动态扩展 | 优点:模型简单,接口易用,在阿里大规模应用;性能好,可大量堆积消息在broker中,支持多种消费,包括集群消费,广播消费,开发活跃度高,版本更新快;缺点:没有在mq核心中实现JMS等接口,有些系统要迁移需要修改大量代码;支持的客户端语言不多,目前是java及c++,其中c++不成熟; | 优点:性能卓越,单机写入TPS约在百万条/秒,最大的优点,就是吞吐量高。在日志领域比较成熟,被多家公司和多个开源项目使用;缺点:Kafka单机超过64个队列/分区,Load会发生明显的飙高现象,队列越多,load越高,发送消息响应时间变长;消费失败不支持重试;支持消息顺序,但是一台代理宕机后,就会产生消息乱序;社区更新较慢; |
RocketMQ概念术语
-
消息的发送与消费模型
主题(Topic)
标识RocketMQ中一类消息的逻辑名字,消息的逻辑管理单位。无论消息生产还是消费,都需要指定Topic。主题主要用于区分消息的种类:一个生产者可以发送消息给一个或者多个Topic,消息的消费者也可以订阅一个或者多个Topic消息。
消息队列 ( Message Queue )
简称Queue或Q。消息物理管理单位。一个Topic将有若干个Q。
无论生产者还是消费者,实际的生产和消费都是针对Q级别。例如Producer发送消息的时候,会预先选择(默认轮询)好该Topic下面的某一条Q发送;Consumer消费的时候也会负载均衡地分配若干个Q,只拉取对应Q的消息。
生产者(Producer)
生产者 :也称为消息发布者,负责发送消息至RocketMQ。
消费者(Consumer)
消费者 :也称为消息订阅者,负责从RocketMQ接收并消费消息。
消费者分组(ConsumerGroup)
标识一类Consumer的集合名称,这类Consumer通常消费一类消息(也称为Consumer Group),且消费逻辑一致。同一个Consumer Group下的各个实例将共同消费topic的消息,起到负载均衡的作用。
订阅关系(Subscription)
RocketMQ中的消费者订阅关系(Subscription)是指消费者与主题(Topic)之间的订阅关系。
消费者可以通过指定订阅规则来订阅某个主题的消息。
RocketMQ生产消费流程
RockeMQ的安装
RocketMQ的windows下的安装
官方下载地址
https://rocketmq.apache.org/zh/download/
控制台下载
环境要求
JDK1.8(64位)
配置环境变量
变量名:ROCKETMQ_HOME
变量值:MQ解压路径\MQ文件夹名
启动
RocketMQ的物理架构中,都是需要先启动NameServer再启动Broker的。所以启动顺序一定不要搞反了。
启动NAMESERVER
Cmd命令框执行进入至‘MQ文件夹\bin’下,然后执行‘start mqnamesrv.cmd’,启动NAMESERVER。成功后会弹出提示框,此框勿关闭。
弹出boot success后,说明启动成功,那么NameServer的监听端口是本机是9876端口。
这种启动方式后,日志的一般放在用户目录下的 logs目录下。
启动BROKER
Cmd命令框执行进入至‘MQ文件夹\bin’下,然后执行
start mqbroker.cmd -n localhost:9876
启动BROKER。成功后会弹出提示框,此框勿关闭。
日志的一般放在用户目录下的 logs目录下。
控制台启动
控制台端口及服务地址配置:
下载完成之后,进入‘\rocketmq-console\src\main\resources’文件夹,打开‘application.properties’进行配置。
因为本身控制台也是单独的Java应用,默认的是8080,为了防止与Tomcat冲突,我改成了8089
然后进入dashboard根目录执行‘mvn clean package -Dmaven.test.skip=true’,编译生成。
执行‘java -jar rocketmq-dashboard-1.0.0.jar’,启动dashboard。
启动脚本整合
每次启动RocketMQ的命令还是挺麻烦的,所以这里做了一个脚本整合。
因为都是采用默认配置,数据文件的地址默认都是在C盘(C盘最好确保有足够的空间,数据文件默认启动就是1G。)
普通消息的发送与消费
三种消息发送方式
同步发送消息
同步发送是指消息发送方发出数据后,同步等待,直到收到接收方发回响应之后才发下一个请求。这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,短信通知。
异步发送消息
异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。消息发送方在发送了一条消息后,不等接收方发回响应,接着进行第二条消息发送。发送方通过回调接口的方式接收服务器响应,并对响应结果进行处理。
单向发送消息
这种方式主要用在不特别关心发送结果的场景,例如日志发送。单向(Oneway)发送特点为发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答。此方式发送消息的过程耗时非常短,一般在微秒级别。
两种消息消费方式
集群消费(负载均衡模式)
消费者采用集群消费方式消费消息,一个分组(Group)下的多个消费者共同消费队列消息,每个消费者处理的消息不同。一个Consumer Group中的各个Consumer实例分摊去消费消息,即一条消息只会投递到一个Consumer Group下面的一个实例。例如某个Topic有3个队列,其中一个Consumer Group 有 3 个实例,那么每个实例只消费其中的1个队列。集群消费模式是消费者默认的消费方式。
这里稍微深入一点,就是集群消费提交的偏移量,持久化是存在broker上的。
广播消费
广播消费模式中消息将对一个Consumer Group下的各个Consumer实例都投递一遍。即使这些 Consumer属于同一个Consumer Group,消息也会被Consumer Group 中的每个Consumer都消费一次。实际上,是一个消费组下的每个消费者实例都获取到了topic下面的每个Message Queue去拉取消费。所以消息会投递到每个消费者实例。
这里稍微深入一点,就是广播消费提交的偏移量,持久化是存在客户端(消费者)的。
两种消费监听方式
消息并发监听
MessageListenerConcurrently:同时开启多个线程并发消费消息。所以这里有可能胡
对于消费的结果,有两种方式:1、成功,2、重试。
消费端如果发生消息失败,没有提交成功(或者直接抛出异常、或者返回null值),消息默认情况下会进入重试队列中。
注意重试队列的名字其实是跟消费群组有关,不是主题,因为一个主题可以有多个群组消费,所以要注意
重试的间隔默认如下:
这里重试机制的大体逻辑是这样,消息一旦重试,比如消息A,那么这个消息就会进入%RETRY%....这个队列,然后看是第几次重试,达到了重试间隔,这个消息才会进行消费者重新投递。所以这种情况很容易导致消息的无序。
消息顺序监听
MessageListenerOrderly:在同一时刻只允许一个线程消费一个队列的消息,并且保证在消费这个队列消息的顺序性。
玩顺序消息时。consume消费消息失败时,不能返回reconsume——later,这样会导致乱序,应该返回suspend_current_queue_a_moment,意思是先等一会(默认1s),一会儿再处理这批消息,而不是放到重试队列里。
该消费线程在消费消息时,会使用锁来保证消息的顺序性。当某个消息消费失败时,该消息所属的队列会进入暂停状态,直到该消息处理成功后才会继续消费下一条消息。
严格使用顺序消息注意事项
但是这里有一个误区,不要认为这么做就可以确保顺序消费,因为这个顺序保障只是确保队列级的。消息在不同的队列中依然是无序的。
所以要做到顺序消费,就必须要创建只能有一个队列的主题。
同时就算一个主题只有一个队列,你也要使用顺序消费监听,如果使用并发消费监听一样会有问题。
分区顺序消息
批量消息的发送与消费
如果发送的消息超过了4M,就需要进行拆分。
过滤消息
Tag过滤
在大多数情况下,TAG是一个简单而有用的设计,其可以来选择您想要的消息。
消费者将接收包含TAGA或TAGB或TAGC的消息。但是限制是一个消息只能有一个标签,这对于复杂的场景可能不起作用。在这种情况下,可以使用SQL表达式筛选消息。SQL特性可以通过发送消息时的属性来进行计算。
Sql过滤
需要修改Broker.conf配置文件。加入enablePropertyFilter=true 然后重启Broker服务
如果抛出错误:说明Sql92功能没有开启
SQL基本语法
RocketMQ定义了一些基本语法来支持这个特性。你也可以很容易地扩展它。
只有使用push模式的消费者才能用使用SQL92标准的sql语句,常用的语句如下:
数值比较: 比如:>,>=,<,<=,BETWEEN,=;
字符比较: 比如:=,<>,IN;
IS NULL 或者IS NOT NULL;
逻辑符号: AND,OR,NOT;
常量支持类型为:
数值,比如:123,3.1415;
字符,比如:'abc',必须用单引号包裹起来;
NULL ,特殊的常量
布尔值,TRUE 或FALSE
消息生产者( 加入消息属性 )
发送消息时,你能通过putUserProperty来设置消息的属性
消息消费者 (使用SQL筛选)
用MessageSelector.bySql来使用sql筛选消息
延时消息
概念介绍
延时消息: Producer 将消息发送到消息队列RocketMQ 服务端,但并不期望这条消息立马投递,而是延迟一定时间后才投递到Consumer 进行消费,该消息即延时消息。
适用场景
消息生产和消费有时间窗口要求:比如在电商交易中超时未支付关闭订单的场景,在订单创建时会发送一条延时消息。这条消息将会在 30 分钟以后投递给消费者,消费者收到此消息后需要判断对应的订单是否已完成支付。如支付未完成,则关闭订单。如已完成支付则忽略。
使用方式
Apache RocketMQ目前只支持固定精度的定时消息,因为如果要支持任意的时间精度,在Broker 层面,必须要做消息排序,如果再涉及到持久化,那么消息排序要不可避免的产生巨大性能开销。(阿里云RocketMQ提供了任意时刻的定时消息功能,Apache的RocketMQ并没有,阿里并没有开源)
发送延时消息时需要设定一个延时时间长度,消息将从当前发送时间点开始延迟固定时间之后才开始投递。
延迟消息是根据延迟队列的level来的,延迟队列默认是
msg.setDelayTimeLevel(3)代表延迟10秒
"1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h"
是这18个等级(秒(s)、分(m)、小时(h)),level为1,表示延迟1秒后消费,level为5表示延迟1分钟后消费,level为18表示延迟2个小时消费。生产消息跟普通的生产消息类似,只需要在消息上设置延迟队列的level即可。消费消息跟普通的消费消息一致。
同时RocketMQ5还支持任意时间的演示