RocketMQ基础学习

RocketMQ基础学习

RocketMQ介绍

  • RocketMQ 是一个队列模型的消息中间件,具有高性能、高可靠、高实时、分布式的特点。支持事务消息、顺序消息、批量消息、定时消息、消息回溯等。

名词解释

  • 生产者(producer): 负责生产消息。RocketMQ通过负载均衡模块选择相应的broker集群队列进行消息投递,投递过程支持快速失败并且低延迟。RocketMQ中的生产者都是以生产者组的形式出现,同一生产者组发送相同topic类型的消息。而一个生产者组是可以同时发送多个主题的消息。
  • 消费者(consumer): 从主题中获取消息的对象。一个消费者同时只能消费一个主题的消息。RocketMQ中的消费者都是以消费者组的形式出现。同一组消费者组消费的是同一个topic的消息。同一消费者组的消费者数量应该小于订阅的topic中queue数量(一个queue中的消息只能被一个消费者消费),超出的消费者不能消费任何消息。
  • 主题(topic): 一类消息的集合。一个主题可以有多条消息,但是每条消息只能属于一个主题。一个topic可以被多个消费者组同时消费。
  • 消息(message ): 所传输数据的物理载体,生产和消费数据的最小单位,每条消息必须属于某一个主题(topic)。
  • 标签(tag): 为消息设置标签,用于同一主题下区分不同类型的消息。如果说主题是一级分类,则标签就相当于二级分类。
  • 队列(queue): 存储消息的物理实体,一个topic中可以有多个queue,queue存储的就是该topic的消息。queue中的消息只能被一个消费者组中的一个消费者消费。
  • 注册中心(NameServer): 是一个broker与topic路由的注册中心,支持Broker的动态注册与发现。负责broker和路由信息管理。接受broker集群的注册信息并且保存下来作为路由信息的基本数据,提供心跳检测机制。每个NameServer中都保存着broker集群的整个路由信息和用于客户端查询的队列信息。producer和consumer通过NameServer可以获取整个broker集群的路由信息,从而进行消息的投递和消费。
  • 消息中转(broker): 负责存储消息、转发消息。接收并存储生成这发送的消息,同时为消费者拉取请求做准备。

工作流程

    1. 启动NameServer,开始监听端口,等待Broker、Producer、Consumer连接。
    1. 启动Broker时,Broker会与所有的NameServer建立长连接,然后每30秒向NameServer定时发送心跳包。
    1. 发送消息。可以在发送消息前手动创建Topic,并指定存储的Broker,然后将关联信息写入NameServer。也可以在发送消息时自动创建Topic。
    1. Producer发送消息。启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取路由信息(当前发送的Topic消息的Queue与Broker的地址映射关系)。选择一个Queue,与队列所在的Broker建立长连接,从而向Broker发消息。
    1. Consumer读取消息。启动时先跟NameServer集群中的其中一台建立长连接,获取其所订阅的Topic路由信息,然后根据路由信息获取需要消费的Queue,跟Broker建立长连接,开始消费其中的消息。

RocketMQ消息模型在这里插入图片描述

相同的ConsumerGroup下的消费者主要有两种负载均衡模式,即广播模式,和集群模式(图中是最常用的集群模式)。

  • 集群模式: 同一个 ConsumerGroup 中的 Consumer 实例是负载均衡消费,如图中 ConsumerGroupA 订阅 TopicA,TopicA 对应 3个队列,则 GroupA 中的 Consumer1 消费的是 MessageQueue 0和 MessageQueue 1的消息,Consumer2是消费的是MessageQueue2的消息。
  • 广播模式: 同一个 ConsumerGroup 中的每个 Consumer 实例都处理全部的队列。需要注意的是,广播模式下因为每个 Consumer 实例都需要处理全部的消息,因此这种模式仅推荐在通知推送、配置同步类小流量场景使用。

RocketMQ工作原理

1.消息的生产
    1. Producer发送消息之前,会先向NameServer发出获取消息Topic的路由信息的请求。
    1. NameServer返回该Topic的路由表(Map结构,key为Topic名称,value是一个QueueData实例列表。一个Broker中该Topic的所有Queue对应一个QueueData。)及Broker列表(Map结构,key为brokerName,value为BrokerData。一个Topic可能存在多个Broker,一个Broker也可能存在不同Topic的Queue)。
    1. Producer根据代码中指定的Queue选择策略,从Queue列表中选出一个队列,用于后续存储消息。
    1. Produer对消息做一些特殊处理,例如,消息本身超过4M,则会对其进行压缩。
    1. Producer向选择出的Queue所在的Broker发出RPC请求,将消息发送到选择出的Queue。
2.Queue选择算法
  • 轮询算法: 默认选择的算法,每个Queue中可以均匀的获取到消息。但是因为一些原因,在某些Broker上的Queue可能投递延迟严重,从而导致Producer的缓存队列中出现较大的消息积压,影响消息的投递性能。
  • 最小投递延迟算法: 每次投递消息,会统计上一次的延迟时间,将消息投递给时间延迟最小的Queue中,延迟时间相同则采用轮询算法。但是该算法会导致延迟小的Queue存在大量的消息,从而使该Queue的Consumer压力增大,降低了消息的消费速度,是MQ中的消息堆积。
3.消息写入

消息进入Broker后到持久化的过程

  • Broker根据queueId获取到该消息对应索引条目要在consumequeue目录中的写入偏移量(queueOffset)。
  • 将queueId、queueOffset等数据与消息一起封装为消息单元。
  • 将消息单元写入到commitlog。(commitlog目录中存放着很多的mappedFile文件,mappedFile文件大小为1G。文件名由20位十进制数构成,表示当前文件的第一条消息的起始位移偏移量。当第一个文件放满时,则会自动生成第二个文件继续存放消息)
  • 同时形成消息索引条目。
  • 将消息索引条目分发到相应的consumequeue中。
4. 消息拉取
  • Consumer获取到其要消费消息所在Queue的消费偏移量offset,计算出其要消费消息的offset(消费offset即消费进度,consumer对某个Queue的消费offset,即消费到了该Queue的第几条消息的消息offset = 消费offset + 1)。
  • Consumer向Broker发送拉取请求,其中会包含其要拉取消息的Queue、消息及消息offset。
  • Broker计算在该Consumequeue中的queueOffset(queueOffset = 消息offset*20字节)。
  • 从该queueOffset处开始向后查找第一个指定Tag的索引条目。
  • 解析该索引条目的前8个字节,即可定位到该消息在commitlog中的commitlog offset。
  • 从对应commitlog offset中读取消息单元,并发送给Consumer。

实例

下载并配置好RocketMQ,启动nameserver注册中心(在rocketmq文件的bin目录下,进入cmd使用如下命令:start mqnamesrv.cmd)和Broker消息中转(start mqbroker.cmd -n 127.0.0.1:9876 -c 【MQ安装路径】/conf/broker.conf)后,才能正常使用。

  • springboot项目中添加依赖
<dependency>
	<groupId>org.apache.rocketmq</groupId>
	<artifactId>rocketmq-spring-boot-starter</artifactId>
	<version>2.0.1</version>
</dependency>
  • 生产者
import org.apache.rocketmq.client.producer.DefaultMQProducer;
 
public class Producer {
    public static void main(String[] args) throws Exception {
        // 创建生产者,并指定组名
        DefaultMQProducer producer = new DefaultMQProducer("group1");
        // 指定Namesrv地址
        producer.setNamesrvAddr("localhost:8888");
        // 启动生产者
        producer.start();
 
        // 创建消息,指定Topic、Tag和消息体
        String message = "Hello, RocketMQ!";
        producer.sendOneway("TopicTest", "TagA", message.getBytes());
        // 批量发送消息(批量消息的Topic必须相同)
 		//List<Message> messages = new ArrayList<>();
        //messages.add(new Message("sanyouTopic", "java日记 0".getBytes()));
        //messages.add(new Message("sanyouTopic", "java日记 1".getBytes()));
        //messages.add(new Message("sanyouTopic", "java日记 2".getBytes()));
        // 发送消息并得到消息的发送结果
        //SendResult sendResult = producer.send(messages);
        // 关闭生产者
        producer.shutdown();
    }
}
  • 消费者
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
 
public class Consumer {
    public static void main(String[] args) throws Exception {
        // 创建消费者,并指定组名
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
        // 指定Namesrv地址
        consumer.setNamesrvAddr("localhost:8888");
        // 订阅Topic和Tag
        consumer.subscribe("TopicTest", "*");
        // 设置回调函数来处理消息
        consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
            for (org.apache.rocketmq.common.message.Message msg : msgs) {
                System.out.println("Received message: " + new String(msg.getBody()));
            }
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        });
        // 启动消费者
        consumer.start();
    }
}
  • 26
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值