RocketMQ分布式消息队列

MQ: 消息队列:

提供消息(请求)服务的中间件 (消息中间件)

生产 存储 消费全过程的软件系统, 先进先出原则

使用场景: 限流削峰, 异步, 解耦, 数据收集, 大数据处理

大项目, 体量大 并发高

好处: 提高系统响应速度, 稳定性

产品: Kafka (多使用在大数据) 瞬时大流量应用 --> 大量的大文件

  RocketMQ --> 大量的小文件

技术选型: 业务场景简单, 允许数据丢失 想要快速上线 --> Redis

  大数据场景, 日志收集, 实时性要求高 --> Kafka

  金融领域, 不能接受消息丢失或重复 --> RabbitMQ, RocketMQ

RocketMQ:

特征: 支持集群, 负载均衡, 水平扩展

NameServer代替Zookeeper,实现服务寻址和服务协调

底层通信框架采用Netty NIO

采用零拷贝的原理,顺序写盘,随机读

RocketMQ官网下载:

RocketMQ   RocketMQ · 官方网站 | RocketMQ

下载地址:下载 | RocketMQ


上传到虚拟机

解压

unzip ./rocketmq-all-4.9.6-bin-release.zip -d /opt/module/

部署NameServer

0.创建文件夹

mkdir backups --> cp ./bin/runserver.sh ./backups/

mkdir -p store store/commitlog store/consumequeue

1.修改jvm占用内存大小runserver.sh

-server -Xms512m -Xmx512m -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m

2.启动NameServer服务:

nohup sh ./bin/mqnamesrv > ./log/nameserver.log 2>&1 &

nohup                           --> 后台启动

sh ./bin/mqnamesrv      --> 启动NameServer服务

> ./log/nameserver.log  --> 信息记录到xx日志

2>&1 &                          --> 后台运行

部署Broker

1.修改jvm占用内存大小runserver.sh

-server -Xms512m -Xmx512m

2.修改broke.conf

# 集群名称,同一个集群下的broker要求统一

brokerClusterName=DefaultCluster

# broker名称

brokerName=broker-a

# brokerId=0代表主节点,大于零代表从节点

brokerId=0

# 删除日志文件时间点,默认凌晨 4 点

deleteWhen=04

# 日志文件保留时间,默认 48 小时

fileReservedTime=48

# Broker 的角色

# - ASYNC_MASTER 异步复制Master

# - SYNC_MASTER 同步双写Master

brokerRole=SYNC_MASTER

# 刷盘方式

# - ASYNC_FLUSH 异步刷盘,性能好宕机会丢数

# - SYNC_FLUSH 同步刷盘,性能较差不会丢数

flushDiskType=SYNC_FLUSH

# 末尾追加,NameServer节点列表,使用分号分割

# 这里的IP需要根据自身情况修改

namesrvAddr=192.168.10.111:9876

# 是否开启自动创建Topic

autoCreateTopicEnable=true

# Broker监听的端口号

listenPort=10911

# 指定消息的存储路径,具体是下面两个

storePathRootDir=/opt/module/rocketmq-4.9.6/store

# commitLog的存储路径

storePathCommitLog=/opt/module/rocketmq-4.9.6/store/commitlog

# 消费队列的存储路径

storePathConsumerQueue=/opt/module/rocketmq-4.9.6/store/consumequeue

3.启动Broker服务:

nohup sh ./bin/mqbroker > ./log/broker.log 2>&1 &

消息的收发

1.设置环境变量:

export NAMESRV_ADDR=192.168.10.111:9876

2.启动消息生产者:

sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer

3.启动消息消费者:

sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer

关闭服务

1.关闭broker:

sh ./bin/mqshutdown broker

2.关闭namesrv

sh ./bin/mqshutdown namesrv

启动Rocket可视化界面的配置

1.配置broker.conf: 防止RocketMQ找不到Broker节点

brokerIP1=192.168.xx.111

2.启动Broker组件

nohup sh ./bin/mqbroker -c ./conf/broker.conf  > ./log/broker.log 2>&1 &

nohup   后台运行

sh ./bin/mqbroker   启动nroker服务

-c ./conf/broker.conf   指定配置文件

> ./log/broker.log  指定输出的日志文件

2>&1 &  后台运行

3.尝试:手动添加topic

./bin/mqadmin updateTopic -n 127.0.0.1:9876 -b 127.0.0.1:10911 -t topic-test

./bin/mqadmin  --> 启动添加组件

updateTopic  --> 更新topic

-n ip地址:端口号  --> 指定nameserver所在的节点ip

-b ip地址:端口号  --> 指定broker所在的节点ip

-t 主题topic-test  --> 指定主题的名称

3.查看所有的主题topic

sh ./bin/mqadmin topicList -n 192.168.10.111:9876

RocketMQ代码实现:

1.依赖: org.apache.rocketmq:rocketmq-client

2.生产者代码:

 a. 创建生产producer组 :

new DefaultMQProducer("设置生产组名"); --> producer

 创建事务消息生产者组: (事务消息的生产者组为这个)

new TransactionMQProducer("设置生产组名");--producer

 b. 设置NameServer地址:

producer.setNamesrvAddr("ip地址:端口号9876") 

 c. 设置消息的长度:

producer.setMaxMessageSize(4096); 

设置超时时间:

producer.setSendMsgTimeout(3000);

设置消息发送失败重试的次数:

producer.setRetryTimesWhenSendAsyncFailed(2);

事务消息需要设置, 事务消息监听器

producer.setTransactionListener( new TransactionListener事务监听器接口 );

--> 事务监听器会重写两个方法: executeLocalTransaction( 执行的本地业务 ) checkTransaction( MQ进行事务状态的回查 )

--> 这两个方法返回值共有三种情况的返回值:

return LocalTransactionState.COMMIT_MESSAGE;--> 提交

return LocalTransactionState.ROLLBACK_MESSAGE;--> 回滚

return LocalTransactionState.UNKNOW;--> 未知

 d. 启动生产者producer 

producer.start();

 e. 创建消息对象:

new Message("主题topic-xxx","tag标签","内容".getBytes(RemotingHelper.DEFAULT_CHARSET));

--> msg  内容需要转换成字节码,否则接收不到内容

还可以给消息定义属性:

msg.setUserProperty("key","value")

 f. 发送消息对象

(同步消息发送)

producer.send(msg);

    (异步消息发送)

producer.send(msg, new SendCallback() {

@Override

public void onSuccess(SendResult sendResult) {

System.out.println(sendResult);

}

@Override

public void onException(Throwable e) {

System.out.println("发送异常:"+e.getMessage());

}

});

(单向消息发送)

producer.sendOneway(msg);

(全局顺序消息发送)

producer.send(msg, new MessageQueueSelector() {

       @Override
       // select方法的参数
       // 第一个参数: 指该Topic下有的队列集合
       // 第二个参数: 发送的消息
       // 第三个参数: 消息将要进入的队列下标,它与send方法的第三个参数相同

    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {

    return mqs.get((Integer) arg);

    }

}, 1); --> 消息进入哪个消息队列

(部分有序消息发送)

--> 模板与全局顺序相同,有小改动

1.多创建几个不同内容的消息  例如:for支付:for发货 

先发送支付消息,再发送发货消息 --> 支付0:发货0 , 支付1:发货1

2.返回的队列需要计算, 订单编号%队列数量, 返回队列, 表示该消息是进入哪个队列

这样相同id的支付和发货就放在同一个队列当中

send()方法个人理解:

1. object o 中的数据是从send()方法的第三个参数中拿取的

2. 第三个参数可以随意定义, 最终影响的的是队列的id, 一般定义为消息id 或 某个队列id

3. 内部类中的方法返回值, 返回的是消息队列id

4. send(参数1消息对象, 参数2队列, 参数3随意): 是将参数1,以参数3为下标放进参数2中

(延迟消息发送)

msg.setDelayTimeLevel(等级数);

-->分为18级, 可以通过修改配置来增加级别, 比如在mq安装目录的 broker.conf 文件中增加

producer.send(msg消息对象);

(批量消息发送)

1.创建消息集合

2.创建多条消息对象, 并放进集合当中

3.producer.send(消息集合)

(事务消息发送)

producer.sendMessageInTransaction(msg消息对象,  Object针对什么消息进行控制参数)

 g.关闭生产者producer实例

producer.shutdown();

返回值结果:

SendResult [

sendStatus=SEND_OK,  -->状态OK

msgId=C0A8006516B018B4AAC270EF9D940000, -->发送者生成的id

offsetMsgId=C0A8006500002A9F0000000000008E1C, -->由Broker生成的消息id

messageQueue=MessageQueue [topic=syn-topic, brokerName=LAPTOP-20VLGCRC, queueId=3], -->队列信息

queueOffset=0 --> offset管理

]

3.消费者代码: -->

 a. 设置消费condumer组 -->

new DefaultMQPushConsumer("设置消费组名");--consumer

 b. 设置NameServer地址 -->

consumer.setNamesrvAddr("127.0.0.1:9876");

 c. 设置消费线程的个数(最大,最小) -->

defaultMQPushConsumer.setConsumeThreadMax(1);

defaultMQPushConsumer.setConsumeThreadMin(1);

设置同时拉取pull多少个消息 -->

defaultMQPushConsumer.setPullBatchSize(1);

 d. 设置消费位置 - 订阅主题 - 指定tag -->

consumer.subscribe("订阅的主题","指定tag标签 || tag标签 || * || …"); 指定消费某条信息

consumer.subscribe("订阅的主题",MessageSelector.bySql("表达式: i>2")); 指定消费某条信息

 e. 设置消息监听器 - 处理消息

-->同步,异步,单项,批量,事务都可以消费:

consumer.registerMessageListener(new MessageListenerConcurrently() {

@Override

public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

   return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

}

});

-->顺序消费:

consumer.registerMessageListener(new MessageListenerOrderly() {

            @Override

            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {

                for (MessageExt mse : list) {

                    System.out.println("线程: " + Thread.currentThread().getName()

                            + "消费了消息, queueId: " + mse.getQueueId()

                            + "--> 消息内容为: " + new String(mse.getBody()));

                }

                return ConsumeOrderlyStatus.SUCCESS;

            }

        });

 e.启动消费者 -->

consumer.start();

 f.关闭消费者 -->

consumer.shutdown();

RocketMQ 原理

1.生产者Producer: 发布消息

2.消费者Condumer: 消费消息

3.Broker: 负责消息的存储,投递,查询,服务的高可用保证

4.NameServer: 注册中心,支持动态注册与发现

主要包括:

-->会保存Broker整个的集群注册信息作为路由基本信息,

用于客户端查询队列的信息,

并控制生产者与消费者的消息发布与消费

-->提供心跳检测机制,检查Broker是否还存活

RocketMQ 核心概念

1.RocketMQ 网络部署特点:

-->Broker一般是以集群的方式出现的

-->Broker是主从集群master中负责处理处理请求, slave负责备份数据

-->当master节点挂掉时, slave会顶上去工作

-->一个master会包含多个slave, 一个slave只有一个master

-->通过相同的BrokerName不同的BrokerId来确定关系 0:主 !0:从

2.RabbitQ工作流程:

-->NameServer启动后会监听端口,等待Producer,Consumer,Broker连接,相当于路由控制中心

-->Broker启动后,会和所有的NameServer建立长连接,

定时发送心跳(Broker的信息:ip+端口+所有的topic信息),这样集群中就会有topic和Broker的映射关系

-->收发信息前会创建topic, 指定存储在哪些Broker上

-->Producer生产者发送消息,启动时先跟NameServer建立长连接, 并从中获取到当前发送的topic存在哪些Broker上,

并轮询从队列列表中选择队列,与队列所在的Broker建立长连接发消息

-->Consumer消费者与生产者类似,获取订阅的topic存在哪些Broker上,并建立长连接,开始消费信息

3.生产者的生产模式

a.Rocket具有多种发送方式: 同步, 异步, 单向, 顺序

--> 其中同步和异步需要返回确认信息

b.Producer生产者都是以组的形式出现 是一类的集合

--> 这类Producer生产者发送相同的topic类型的消息

--> 一个Prosucer组可以同时发送多个topic主题的消息

--> 会使用算法把消息发送到哪个master中的队列

4.消费者的消费模式

a.拉取式消费、推动式消费。(主动,被动)

b.Consumer消费者都是以组的形式出现 是一类的集合

--> 这类Consumer消费者消费相同的topic类型的消息

--> 一个Consumer组可以同时消费多个topic主题的消息

--> 一个队列是不能被同一个消费者组中的多个消费者进行消费的, 为了减少资源竞争,提升整体的性能

5.topic消息主题

表示一类消息的集合, 每个主题都包含若干条message消息,

--> 主题是消息订阅(消费)的基本单位

6.message消息

消息所传输的物理载体, 生产和消费的最小单位, 每条消息都必须属于一个主题

7.tag标签

为消息设置标记,区分同一主题下不同类型的消息

-->优化查询系统,可以扩展

-->主题是一级分类, 标签是二级分类

8.MessageQueue消息队列

一个主题包含多个队列,也被称之为主题中的消息分区

一个队列最多只能分配给一个消费者

一个消费者得到多个队列

-->避免消费过程中的多线程处理和资源锁定,有效提高消费者的处理效率

-->消费者组中的Consumer数量应该小于等于le主题中队列的数量,为了减少不必要的资源

-->为了防止消息紊乱一个消费者组中的Consumer都是订阅相同主题下的队列

9.消息,主题,队列,组之间的关系:

-->一个组可以消费或发送多个topic主题的消息

-->一个队列只能有一个消费者进行消费

-->一条消息只能属于一个主题

-->一个主题包含多个队列

-->一个消费者得到多个队列

10.读写队列

Queue分为 写队列 和 读队列

默认创建数量是都是4

Producer发送的消息进入写队列,Consumer从读队列获取数据,一般情况下读写队列数量是一样的

可以通过可视化界面修改Topic中的队列数量

perm: 设置对当前创建Topic的操作权限: 2可写 4可读 6可读写

11.MessageId/Key

每个消息拥有唯一的MessageId,方便查询

MessageId有两个,生产者发送时自动生成一个,到达Broker之后,Broker也会自动生成一个

12.Rebalance重新负载

当消费者的数量或者队列的数量修改,

他会把一个主题下的多个队列重新分配给消费者组下的Consumer, 目的增加消费能力

13.消息拉取模式

拉取式 pull ,和推送是 push

pull: 需要消费者间隔一定时间就去遍历关联的Queue,实时性差但是便于应用控制消息的拉取

Push: 推送式,封装了所有的Queue,实时性强,但是对系统资源占用比较多

14.消息消费模式: 针对同一个消费者组下的所有的消费者

广播模式: 都会受到同一个主题下的所有消息, 可能会出现脏读

集群模式: 平分同一个主题下的所有消息, 安全

15.Queue的分配算法: Queue是如何分配给Consumer的

平均分配【默认】: 队列数量除以消费者, 有余数再逐个分配.

环形平均策略: 根据消费者的顺序,一个一个的分配Queue即可类似于发扑克牌.

一致性Hash策略: 该算法将Consumer的Hash值作为节点放到Hash环上,然后将Queue的hash值也放入Hash环上,通过顺时针进行就近分配.

同机房策略:该算法会根据queue的部署机房位置和consumer的位置,过滤出当前consumer相同机房的queue。

然后按照平均分配策略或环形平均策略对同机房queue进行分配。

如果没有同机房queue,则按照平均分配策略或环形平均策略对所有queue进行分配。

-->平均分配性能比较高,一致性Hash性能不高,但是能减少Rebalance,如果Consumer数量变动频繁可以使用一致性Hash

16.Offset管理

用来维护Consumer的消费进度

-->consume_from_last_offset: 从队列的最后一条消息开始消费

-->consume_from_first_offset:从队列的第一条信息开始消费

-->consume_from_timestamp:从某个时间戳位置开始消费信息

消费者消费结束后,会向Consumer提交消费进度offset给Broker

offset信息存储分别为:

-->本地offset管理: json形式持久化到Consumer本地磁盘,用户目录下,适用于广播模式

-->远程offset管理: store/config/consumerOffset.json文件以json方式存储,适用于集群模式

集群模式下,消费者消费完信息后会向Broker提交消费进度:

提交的方式有两种:

-->同步提交:需要等待上一批消息的Broker的成功响应后,才可以继续读取下一批消息进行消费,

超时则重新提交,直到获取成功响应,没有收到响应,则会重新提交

严重影响消费者的吞吐量

-->异步提交:无需等待上一批的消息的Broker成功响应, 可以直接读取并消费下一批消息,

增加了消费者的吞吐量,

虽然不需要等待,但Broker还是会向消费者进行响应

17.消息的清理

消息不会被单独清理,消息是顺序存储到commitlog中,以commitlog为单位进行清理

默认72h后进行清理

到达时间清理点,自动清理过期文件: 默认凌晨4点

磁盘空间使用率达到过期清理阈值(75%)-自动清理过期文件,(85%)-按照设定的规则清理文件,(90%)-拒绝写入数据

18.延迟消息

1.Broker在存储生产者写入消息时,首先会将其写入到CommitLog中

判断有没有延时等级:

2.没有: 转发到目标队列

  有: 将消息topic名字改为SCHEDULE_TOPIC_XXXX

根据延迟等级在consumequeue目录中SCHEDULE_TOPIC_XXXX主题下创建出相应的queueId

投递时间 = 消息存储时间 + 延时等级时间

等延迟时间到了, 消息还会进入commitlog文件中, 再提交到目标队列中

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值