Windows系统搭建RocketMQ

准备条件

  1. windows系统
  2. JDK 8以上
  3. 下载Binary版本的rocketmq-all-4.4.0-bin-release(下载链接:https://rocketmq.apache.org/)
  4. 下载RocketMQ的可视化项目rocketmq-console工程(地址:https://github.com/apache/rocketmq-externals)

下载RocketMQ以及打开

  1. 下载Binary版本
  2. 解压至没有空格的文件夹,例如(D:\application\rocketmq-all-4.7.0-bin-release)
  3. cmd命令下切换路径到该文件夹下的bin目录(cd D:\application\rocketmq-all-4.7.0-bin-release\bin)
  4. 启动mqnamesrv: start mqnamesrv.cmd
    注意点:
    修改默认的xms xmx堆大小(D:\application\rocketmq-all-4.7.0-bin-release\bin的runServer的脚本)
    在这里插入图片描述
  5. 启动mqbroker: mqbroker -n localhost:9876 autoCreateTopicEnable=true
    注意点:
    如果出现 无法加载主类 则修改(D:\application\rocketmq-all-4.7.0-bin-release\bin的runBroker的脚本)
    在这里插入图片描述

下载RocketMQ的可视化项目

  1. 从https://github.com/apache/rocketmq-externals该地址clone下来代码
  2. 修改配置文件application.properties的rocketmq.config.namesrvAddr=127.0.0.1:9876
  3. 运行该项目并访问http://localhost:8080/#/
    在这里插入图片描述

代码层面测试

pom.xml

		<dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-common</artifactId>
            <version>4.7.0</version>
        </dependency>

Untils

import org.apache.commons.lang3.RandomUtils;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Description RocketMq连接工具类,提供消息发送与拉取功能
 */
public class RocketMqUtils {

    private static Logger logger = LoggerFactory.getLogger(RocketMqUtils.class);

    private String namesrvAddr;
    private String producerGroup;
    private String consumerGroup;
    private String topic;
    private String tags;
    private int timeout;

    /**
     * 初始化工具类
     * @param namesrvAddr   rocketmq服务地址,多个用;号隔开
     * @param topic         主题
     * @param tags           标识,多个用||号隔开
     * @param timeout       发送超时时长
     */
    public RocketMqUtils(String namesrvAddr,  String topic, String tags, int timeout){
        this.namesrvAddr = namesrvAddr;
        this.topic = topic;
        this.tags = tags;
        this.timeout = timeout;
    }

    /**
     * 创建生产者对象
     * @return
     */
    private DefaultMQProducer createMQProducer(){
        //生产者的组名
        DefaultMQProducer producer = new DefaultMQProducer(producerGroup);
        //指定NameServer地址,多个地址以 ; 隔开
        producer.setNamesrvAddr(namesrvAddr);
        //发送消息超时时长设置
        producer.setSendMsgTimeout(timeout);
        //发送失败,重试三次
        //producer.setRetryTimesWhenSendFailed(3);
        return producer;
    }

    /**
     * 发送MQ数组消息
     * @param producerGroup 生产组
     * @param msgList       消息集合
     * @return
     * @throws MQClientException
     * @throws Exception
     */
    public List<SendResult> sendMq(String producerGroup, List<String> msgList) throws MQClientException, Exception {
        return this.sendMq(producerGroup, msgList, false, false, 0);
    }

    /**
     * 发送MQ数组消息
     * @param producerGroup     生产组
     * @param msgList           消息集合
     * @param isBatch           是否批处理
     * @param isOrder           是否为顺序消息
     * @param delayTimeLevel    消息等级(延后发送),为0则立即发送,等级从1到10
     * @return
     * @throws MQClientException
     * @throws Exception
     */
    public List<SendResult> sendMq(String producerGroup, List<String> msgList, boolean isBatch, boolean isOrder, int delayTimeLevel) throws MQClientException, Exception {
        this.producerGroup = producerGroup;
        if (msgList == null || msgList.size()<=0){
            throw new RuntimeException("send rocket topic<" + topic + "> mq message to List<String> is null ...");
        }
        DefaultMQProducer producer = this.createMQProducer();
        List<Message> messageList = new ArrayList<Message>();
        //生成随机数,保证本批消息发送到topic同一个索引位queue,推荐使用具有业务意义的编号,如:订单号,保证同一个订单业务发送到同一个队列中
        int order = RandomUtils.nextInt(1000, 10000);
        //封装到message对象中
        for (String msg : msgList){
            //delayTimeLevel:定时消息等级,指定的时间后才能传递,不支持任意精度时间。按level等级划分,默认从level=1开始,如果level=0则不延时,具体如messageDelayLevel值
            //messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
            Message message = new Message(topic, tags, msg.getBytes(RemotingHelper.DEFAULT_CHARSET));
            message.setDelayTimeLevel(delayTimeLevel);
            messageList.add(message);
            logger.info("send message body:{}", message.toString());
        }
        try {
            producer.start();
            //是否批量发送,批量发送消息可提高传递小消息的性能
            if (isBatch) {
                return this.sendMqAlikeList(producer, messageList, isOrder, order);
            }else {
                return this.sendMqDisaffinityList(producer, messageList, isOrder, order);
            }
        }  catch (MQClientException mqce) {
            logger.error("send rocket topic<" + topic + "> rocketMq client error:", mqce);
            throw mqce;
        }  catch (Exception e) {
            logger.error("send rocket topic<" + topic + "> rocketMq send msg error:", e);
            throw e;
        } finally {
            //关闭生产者
            producer.shutdown();
        }
    }

    /**
     * 快速或顺序发送消息
     * @param producer
     * @param messageList
     * @param isOrder
     * @param order
     * @return
     * @throws Exception
     */
    private List<SendResult> sendMqDisaffinityList(DefaultMQProducer producer, List<Message> messageList, boolean isOrder, int order) throws Exception{
        List<SendResult> sendResultList = new ArrayList<SendResult>();
        for (Message m : messageList){
            try {
                if (isOrder){
                    //顺序发送,适用场景:订单的下单、待支付、已支付、待打包、已发货等,一定要保证消息顺序消费
                    //对所有队例总数取模计算,获得索引位上的队列
                    sendResultList.add(producer.send(m, (list, message, o) -> list.get((Integer) o % list.size()), order));
                }else {
                    sendResultList.add(producer.send(m));
                }
            } catch (MQClientException e) {
                e.printStackTrace();
            } catch (RemotingException e) {
                e.printStackTrace();
            } catch (MQBrokerException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return sendResultList;
    }

    /**
     * 批量或批量顺序发送消息
     * 使用限制:同一批次的消息应具有:相同的主题,相同的waitStoreMsgOK,并且不支持计划。一批消息的总大小不得超过1M
     * @param producer
     * @param messageList
     * @param isOrder
     * @param order
     * @return
     * @throws Exception
     */
    private List<SendResult> sendMqAlikeList(DefaultMQProducer producer, List<Message> messageList, boolean isOrder, int order) throws Exception{
        if (isOrder){
            //顺序发送,适用场景:订单的下单、待支付、已支付、待打包、已发货等,一定要保证消息顺序消费
            //注意:如果新的topic,此处获取topic下的队列会出错,请先建立topic
            List<MessageQueue> list = producer.fetchPublishMessageQueues(topic);
            //对所有队例总数取模计算,获得索引位上的队列
            return Arrays.asList(producer.send(messageList, list.get(order % list.size())));
        }else {
            return Arrays.asList(producer.send(messageList));
        }
    }

    /**
     * 发送MQ消息(单个消息可使用此方法)
     * @param producerGroup     生产组
     * @param msg               消息
     * @return
     * @throws Exception
     */
    public SendResult sendMq(String producerGroup,String msg) throws Exception{
        return this.sendMq(producerGroup, Arrays.asList(msg)).get(0);
    }

    /**
     * 创建消费对象
     * @param isBroadcasting 是否为广播消费模式
     * @return
     * @throws MQClientException
     */
    private DefaultMQPushConsumer createMqConsumer(boolean isBroadcasting) throws MQClientException{
        //消费者的组名
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup);
        //指定NameServer地址,多个地址以 ; 隔开
        consumer.setNamesrvAddr(namesrvAddr);
        //订阅PushTopic下Tag为push的消息
        consumer.subscribe(topic, tags);
        //设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费
        //如果非第一次启动,那么按照上次消费的位置继续消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        //设置消费模式为广播模式,即每个消费者都能接收到所有消息
        if (isBroadcasting) {
            consumer.setMessageModel(MessageModel.BROADCASTING);
        }
        return consumer;
    }

    /**
     * 监听MQ队列,拉取MQ消息并进行消费
     * @param consumerGroup     消费组
     * @param headerInterface   回调方法
     * @throws MQClientException
     * @throws Exception
     */
    public void pullMq(String consumerGroup, HeaderInterface headerInterface) throws MQClientException, Exception {
        this.pullMq(consumerGroup, headerInterface, false, false);
    }
    /**
     * 监听MQ队列,拉取MQ消息并进行消费(支持广播消费模式)
     * @param consumerGroup     消费组
     * @param headerInterface   回调方法
     * @param isBroadcasting    是否为广播消费模式
     * @param isOrder           是否为顺序消息
     * @throws MQClientException
     * @throws Exception
     */
    public void pullMq(String consumerGroup, HeaderInterface headerInterface, boolean isBroadcasting, boolean isOrder) throws MQClientException, Exception {
        this.consumerGroup = consumerGroup;
        //消费者的组名
        DefaultMQPushConsumer consumer = this.createMqConsumer(isBroadcasting);
        try {
            if (isOrder){
                //顺序消费
                consumer.registerMessageListener(this.setOrderlyConsumeMessage(headerInterface));
            }else {
                //普通消费
                consumer.registerMessageListener(this.setConcurrentlyConsumeMessage(headerInterface));
            }
            consumer.start();
        } catch (Exception e) {
            logger.error("rocketMq pull msg error:", e);
            throw e;
        } finally {
            this.addShutdownHook(consumer);
        }
    }

    /**
     * 消费端默认消息
     * @param headerInterface
     * @return
     */
    private MessageListenerConcurrently setConcurrentlyConsumeMessage(HeaderInterface headerInterface){
        return (List<MessageExt> list, ConsumeConcurrentlyContext context) -> {
            try {
                for (MessageExt message : list) {
                    headerInterface.execute(message);
                }
            } catch (Exception e) {
                logger.error("rocketMq consumer pull msg error:", e);
                //稍后再试
                return ConsumeConcurrentlyStatus.RECONSUME_LATER;
            }
            //消费成功
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        };
    }

    /**
     * 消费端顺序消息
     * @param headerInterface
     * @return
     */
    private MessageListenerOrderly setOrderlyConsumeMessage(HeaderInterface headerInterface){
        return (list, context) -> {
            try {
                for (MessageExt message : list) {
                    headerInterface.execute(message);
                }
            } catch (Exception e) {
                logger.error("rocketMq consumer pull msg error:", e);
                //稍后再试
                return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
            }
            //消费成功
            return ConsumeOrderlyStatus.SUCCESS;
        };
    }

    /**
     * 函数式回调接口
     */
    @FunctionalInterface
    interface HeaderInterface{
        void execute(MessageExt message) throws IOException;
    }

    /**
     * 注册虚拟机器关机钩子事件
     * @param consumer
     */
    private void addShutdownHook(DefaultMQPushConsumer consumer){
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            logger.info("jvm shutdown hook: close rocketMq consumer server ...");
            consumer.shutdown();
        }));
    }

    public static void main(String[] args) throws Exception {
        // tag="pushA || pushB || pushC" 用||隔开表示多个tag消息都可以接收,null或*表示主题所有队列,但是发送MQ一条消息只能有一个tag标签
        RocketMqUtils rocketMqUtils = new RocketMqUtils("192.168.1.1:9876;192.168.1.2:9876", "test-topic-04", "pushA",3000);
        //生产消息
        List<String> msgList = new ArrayList<String>();
        for (int i = 0;i<10;i++){
            msgList.add("order_" + UUID.randomUUID().toString());
            if ((i + 1) % 10 == 0) {
                TimeUnit.SECONDS.sleep(3);
            }
        }
        //发送同步消息
        rocketMqUtils.sendMq("cluster-test-group", msgList, false, true, 3);

        //消费消息,如果多个消费者线程(应用)consumerGroup相同,则天然具备了负载均衡消费topic消息能力
//        rocketMqUtils.pullMq("test-consumer-group", (message)->{
//            String messageBody = new String(message.getBody(), RemotingHelper.DEFAULT_CHARSET);
//            System.out.println("消费响应:tag : " + message.getTags() + ",  msgBody : " + messageBody);//输出消息内容
//        }, true);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值