RocketMQ详解

RocketMQ

简介
在这里插入图片描述

如图所示为RocketMQ基本的部署结构,主要分为NameServer集群、Broker集群、Producer集群和Consumer集群四个部分。

大致流程
Broker在启动的时候会去向NameServer注册并且定时发送心跳,Producer在启动的时候会到NameServer上去拉取Topic所属的Broker具体地址,然后向具体的Broker发送消息

为了消除单点故障,增加可靠性或增大吞吐量,可以在多台机器上部署多个nameserver和broker,并且为每个broker部署1个或多个slave

NameServer

NameServer的作用是Broker的注册中心。

Topic、Tag、Queue、GroupName

Topic 与 Tag 都是业务上用来归类的标识,区分在于 Topic 是一级分类,而 Tag 可以理解为是二级分类。

1)Topic

​ Topic翻译为话题。我们可以理解为第一级消息类型,比如一个电商系统的消息可以分为:交易消息、物流消息等,一条消息必须有一个Topic。

2)Tag

标签,意思就是子主题,可以理解为第二级消息类型,交易创建消息,交易完成消息… 一条消息可以没有Tag

3)Queue

一个topic下,可以设置多个queue(消息队列),默认4个队列。当我们发送消息时,需要要指定该消息的topic。
RocketMQ会轮询该topic下的所有队列,将消息发送出去。

4)groupName

RocketMQ中也有组的概念。代表具有相同角色的生产者组合或消费者组合,称为生产者组或消费者组。

RocketMQ下载与安装

下载
官方网站下载:http://rocketmq.apache.org/,我们找到 Quick Start 进入快速开始页面找到下载链接

http://rocketmq.apache.org/docs/quick-start/
在这里插入图片描述
安装
1.将下载好的文件解压到D:\coding-software目录下,
2.配置环境变量ROCKETMQ_HOME=D:\coding-software\rocketmq-all-4.4.0-bin-release

在这里插入图片描述

windows启动操作

启动NAMESERVER
cmd命令框执行进入至‘MQ文件夹\bin’下,然后执行start mqnamesrv.cmd

start mqnamesrv.cmd

启动NAMESERVER。成功后会弹出提示框,此框勿关闭。
在这里插入图片描述
启动BROKER
1.cmd命令框执行进入至‘MQ文件夹\bin’下,然后执行start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true

start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true

启动BROKER。成功后会弹出提示框,此框勿关闭
在这里插入图片描述
注意:假如弹出提示框提示‘错误: 找不到或无法加载主类 xxxxxx’。打开runbroker.cmd,然后将‘%CLASSPATH%’加上英文双引号。保存并重新执行start语句。
在这里插入图片描述

RocketMQ插件部署

RocketMQ插件是一个基于SpringBoot编写的可视化插件,主要用于对RocketMQ提供了可视化的管理界面。

下载

下载地址:https://github.com/apache/rocketmq-externals/releases

编译启动

先修改下配置文件的nameserver地址
修改rocketmq-console\src\main\resources\application.properties,修改如下:
在这里插入图片描述
进入‘\rocketmq-externals\rocketmq-console’文件夹

mvn clean package -Dmaven.test.skip=true

编译生成。
在这里插入图片描述
编译成功之后,Cmd进入‘target’文件夹,执行

java -jar rocketmq-console-ng-1.0.0.jar

启动‘rocketmq-console-ng-1.0.0.jar’。

测试

输入地址http://127.0.0.1:8080/#/ops
在这里插入图片描述

RocketMQ入门

步骤分析

RocketMQ消息消费类型有2种类型,
集群消息模式:集群模式也就是消息只能同时被一个消费者读取
广播模式。广播模式则可以同时被所有消费者读取。
消息发送步骤:

1.创建DefaultMQProducer
2.设置Namesrv地址
3.启动DefaultMQProducer
4.创建消息Message
5.发送消息,发送到broker
6.关闭DefaultMQProducer

消息消费步骤:

1.创建DefaultMQPushConsumer
2.设置namesrv地址
3.设置subscribe,这里是要读取的主题信息
4.创建消息监听MessageListener
5.启动消费
6.获取消息信息
7.返回消息读取状态

依赖:

 <dependencies>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

常量类:

public class SysConstants {
	//nameserver的地址
    public static final String NAME_SERVER = "127.0.0.1:9876";
}

普通消息

消息生产者

创建类Producer, main方法代码如下:

/**
 * 普通消息生产者
 */
public class Producer {
    public static void main(String[] args) throws Exception {
//        创建一个消息发送入口对象,主要用于消息发送,指定生产者组
        DefaultMQProducer producer = new DefaultMQProducer("producerGroup");
//        设置NameServe地址,如果是集群环境,用分号隔开
        producer.setNamesrvAddr(SysConstants.NAME_SERVER);
//        启动并创建消息发送组件
        producer.start();
//        topic的名字
        String topic = "rocketDemobasic";
//        标签名
        String tag = "tag1";
//        要发送的数据
        String body = "hello,RocketMq,basic1";
        Message message = new Message(topic,tag,body.getBytes());
        // 发送消息
        SendResult result = producer.send(message,20000);
        System.out.println(result);

//        关闭消息发送对象
        producer.shutdown();
    }
}
消息消费者

创建类Consumer,main方法代码如下:

/**
 * 普通消息消费者
 */
public class Consumer {

    public static void main(String[] args) throws Exception {
//        创建一个消费管理对象,并创建消费者组名字
        DefaultMQPushConsumer consumerGroup = new DefaultMQPushConsumer("ConsumerGroup");
//        设置NameServer地址,如果是集群环境,用逗号分隔
        consumerGroup.setNamesrvAddr(SysConstants.NAME_SERVER);
//        设置要读取的消息主题和标签
        consumerGroup.subscribe("rocketDemobasic","*");
//        设置消费者 可以消费的消息条数
        consumerGroup.setConsumeMessageBatchMaxSize(1);
//        读取消息的位置
        //ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET:从最后一个读取
        //ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET:从第一个读取
        consumerGroup.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//      设置回调函数,处理消息
        consumerGroup.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                                                            ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                try{
//                    读取消息记录
                    for (MessageExt messageExt : msgs) {
//                    获取消息主题
                        String topic = messageExt.getTopic();
//                    获取消息标签
                        String tags = messageExt.getTags();
//                    获取消息体内容
                        String body = new String(messageExt.getBody(), "UTF-8");
                        System.out.println("topic:"+topic+",tags:"+tags+",body:"+body);
                    }

                }catch(Exception e){
                    e.printStackTrace();
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
//      运行消息消费对象
        consumerGroup.start();
    }
}

控制台:
在这里插入图片描述

顺序消息

消息有序指的是可以按照消息的发送顺序来消费。RocketMQ是通过将“相同ID的消息发送到同一个队列,而一个队列的消息只由一个消费者处理“来实现顺序消息
如何保证顺序

在MQ的模型中,顺序需要由3个阶段去保障:
	1.消息被发送时保持顺序
	2.消息被存储时保持和发送的顺序一致
	3.消息被消费时保持和存储的顺序一致

在这里插入图片描述

生产者
1.Producer端确保消息顺序唯一要做的事情就是将消息路由到特定的队列

我们创建一个消息生产者OrderProducer
Producer端确保消息顺序唯一要做的事情就是将消息路由到特定的队列,在RocketMQ中,通过MessageQueueSelector来实现分区的选择

比如如下实现就可以保证相同的订单的消息被路由到相同的分区:

long orderId ; //订单号  
return mqs.get(orderId % mqs.size()); //用订单号 和队列数量 取模

准备顺序消息:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    private Long orderId;
    private String desc;
    public static List<Order> buildOrders(){
        List<Order> list = new ArrayList<>();
        Order order1001a = new Order(1001L,"创建");
        Order order1004a = new Order(1004L,"创建");
        Order order1006a = new Order(1006L,"创建");
        Order order1009a = new Order(1009L,"创建");
        list.add(order1001a);
        list.add(order1004a);
        list.add(order1006a);
        list.add(order1009a);
        Order order1001b = new Order(1001L,"付款");
        Order order1004b = new Order(1004L,"付款");
        Order order1006b = new Order(1006L,"付款");
        Order order1009b = new Order(1009L,"付款");
        list.add(order1001b);
        list.add(order1004b);
        list.add(order1006b);
        list.add(order1009b);
        Order order1001c = new Order(1001L,"完成");
        Order order1006c = new Order(1006L,"完成");
        list.add(order1001c);
        list.add(order1006c);
        return list;
    }
}
消息生产者代码:
/**
 * 顺序消息生产者
 */
public class ProducerOrder {
        //nameserver地址
        private static String namesrvaddress= SysConstants.NAME_SERVER;
        public static void main(String[] args) throws Exception {
            //创建DefaultMQProducer
            DefaultMQProducer producer = new DefaultMQProducer("order_producer");
            //设置namesrv地址
            producer.setNamesrvAddr(namesrvaddress);
            //启动Producer
            producer.start();
            List<Order> orderList = Order.buildOrders();
            for (Order order : orderList) {
                String body = order.toString();
                //创建消息
                Message message = new Message("orderTopic","order",body.getBytes());
                //发送消息
                SendResult sendResult = producer.send(
                        message,
                        new MessageQueueSelector() {
                            /**
                             * @param mqs topic中的队列集合
                             * @param msg 消息对象
                             * @param arg 业务参数
                             * @return
                             */
                            @Override
                            public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                                //参数是订单id号
                                Long orderId = (Long) arg;
                                //确定选择的队列的索引
                                long index = orderId % mqs.size();
                                return mqs.get((int) index);
                            }
                        },
                        order.getOrderId(),
                        20000);
                System.out.println("发送结果="+sendResult);

            }
            //关闭Producer
            producer.shutdown();
        }
    }
消费者

创建一个消息消费者OrderConsumer,消息监听用MessageListenerOrderly来实现顺序消息,
代码如下:

/**
 * 顺序消息 消费者
 */
public class ConsumerOrder {
    public static void main(String[] args) throws Exception {

        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_consumer");
        consumer.setNamesrvAddr(SysConstants.NAME_SERVER);
        //从第一个开始消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

        consumer.subscribe("orderTopic","*");

        consumer.registerMessageListener(new MessageListenerOrderly() {
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println("当前队列:"+context.getMessageQueue().getQueueId()+",接收消息:"+new String(msg.getBody()));
                }
                return ConsumeOrderlyStatus.SUCCESS;
            }
        });
        consumer.start();
        System.out.printf("消费者启动成功.%n");
    }
}

延迟消息

使用限制

rocketmq支持固定延迟等级
//“1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”

生产者
/**
 * 延迟消息 生产者
 */
public class ProducerDelay {

    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("delay_producer");
        producer.setSendMsgTimeout(10000);
        //设置nameserver
        producer.setNamesrvAddr(SysConstants.NAME_SERVER);
        //生产者开启
        producer.start();
        //创建消息对象
        Message message = new Message("delayTopic","delay","hello world".getBytes());
        //设置延迟时间级别
        message.setDelayTimeLevel(2);
        //发送消息
        SendResult sendResult = producer.send(message,20000);
        System.out.println(sendResult);
        //生产者关闭
        producer.shutdown();
    }
}
消费者
/**
 * 延迟消息 消费者
 */
public class ConsumerDelay {
    public static void main(String[] args) throws Exception {
        //创建消费者对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("delay_consumer");
        //设置nameserver
        consumer.setNamesrvAddr(SysConstants.NAME_SERVER);
        //设置主题和tag
        consumer.subscribe("delayTopic","*");
        //注册消息监听
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    long now = System.currentTimeMillis();
                    long storeTime = msg.getStoreTimestamp();
                    long dealy = now - storeTime;
                    Date date = new Date(storeTime);
                    Date nowdate = new Date(now);
                    System.out.println("now="+now);
                    System.out.println("storeTime="+storeTime);
                    System.out.println("delay="+dealy);
                    System.out.println("date="+date);
                    System.out.println("nowdate="+nowdate);
                    System.out.println("消息ID:"+msg.getMsgId()+",延迟时间:"+dealy);
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //开启消费者
        consumer.start();
        System.out.println("消费者启动");
    }
}

批量发送消息

上面发送消息,我们测试的时候,可以发现消息只有一个消费者能收到,如果我们想实现消息广播,让每个消费者都能收到消息也是可以实现的。而且上面发送消息的时候,每次都是发送单条Message对象,能否批量发送呢?答案是可以的。

生产者
/**
 * 批量 生产者
 */
public class ProducerBatch {

    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("batch_producer");
        //设置nameserver
        producer.setNamesrvAddr(SysConstants.NAME_SERVER);
        //生产者开启
        producer.start();
        //创建消息对象  集合
        String topic = "batchTopic";
        String tag = "batchTag";
        List<Message> messageList = new ArrayList<>();
        Message message1 = new Message(topic,tag,"hello world1".getBytes());
        Message message2 = new Message(topic,tag,"hello world2".getBytes());
        Message message3 = new Message(topic,tag,"hello world3".getBytes());
        messageList.add(message1);
        messageList.add(message2);
        messageList.add(message3);
        //发送消息
        SendResult sendResult = producer.send(messageList,20000);
        System.out.println(sendResult);
        //生产者关闭
        producer.shutdown();
    }
}

注意:发送的总消息长度 不能大于4M。

消费者
/**
 * 批量消费者
 */
public class ConsumerBatch {
    public static void main(String[] args) throws Exception {
        //创建消费者对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("batch_consumer");
        //设置nameserver
        consumer.setNamesrvAddr(SysConstants.NAME_SERVER);
        //设置主题和tag
        consumer.subscribe("batchTopic","*");
        //注册消息监听
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println("消息ID:"+msg.getMsgId()+","+msg.getTopic()+","+msg.getTags());
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //开启消费者
        consumer.start();
        System.out.println("消费者启动");
    }
}

广播模式和集群模式

集群模式,只有一个消费者可以收到消息
广播模式,所有的消费者都可以收到消息。

需要把【消息模式】设置为 :广播模式,同时开启2个以上的消费者,再次运行生产者,只需要在消费方设置一下消费模式即可。

MessageModel.BROADCASTING:广播模式
MessageModel.CLUSTERING:集群模式

需要在消费者加入的代码如下:

//设置消费模式为广播模式(BROADCASTING),默认为集群模式(CLUSTERING)
consumer.setMessageModel(MessageModel.CLUSTERING);

注意:2个消费者的 名字都是一样的。

消费者1
/**
 * 批量消费者
 */
public class ConsumerBroadcast {
    public static void main(String[] args) throws Exception {
        //创建消费者对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("delay_consumer");
        //设置nameserver
        consumer.setNamesrvAddr(SysConstants.NAME_SERVER);
        //设置主题和tag
        consumer.subscribe("broadcastTopic","*");
        //设置消息模式 为 广播模式
        consumer.setMessageModel(MessageModel.BROADCASTING);
        //注册消息监听
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println("消费者1:消息ID:"+msg.getMsgId()+",内容"+new String(msg.getBody()));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //开启消费者
        consumer.start();
        System.out.println("消费者启动");
    }
}
消费者2
/**
 * 批量消费者
 */
public class ConsumerBroadcast1 {
    public static void main(String[] args) throws Exception {
        //创建消费者对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("delay_consumer");
        //设置nameserver
        consumer.setNamesrvAddr(SysConstants.NAME_SERVER);
        //设置主题和tag
        consumer.subscribe("broadcastTopic","*");
        //设置消息模式 为 广播模式
        consumer.setMessageModel(MessageModel.BROADCASTING);
        //注册消息监听
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println("消费者2:消息ID:"+msg.getMsgId()+",内容"+new String(msg.getBody()));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //开启消费者
        consumer.start();
        System.out.println("消费者启动");
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值