RocketMQ常用的几种消息队列形式

用锹的人比手厉害,会做锹的人比用锹的厉害
记录几种rocketmq的几种形式,以及基于springboot的常见的配置方式

这是几个mq的相对对比

在这里插入图片描述
先贴一下配置


@Slf4j
@SpringBootConfiguration
public class MQProducerConfiguration {
    /**
     * 发送同一类消息的设置为同一个group,保证唯一,默认不需要设置,rocketmq会使用ip@pid(pid代表jvm名字)作为唯一标示
     */
    @Value("${rocketmq.producer.groupName}")
    private String normalGroupName;
    @Value("${rocketmq.producer.transation.groupName}")
    private String transationGroupName;
    @Value("${rocketmq.producer.namesrvAddr}")
    private String namesrvAddr;
    /**
     * 消息最大大小,默认4M
     */
    @Value("${rocketmq.producer.maxMessageSize}")
    private Integer maxMessageSize;
    /**
     * 消息发送超时时间,默认3秒
     */
    @Value("${rocketmq.producer.sendMsgTimeout}")
    private Integer sendMsgTimeout;
    /**
     * 消息发送失败重试次数,默认2次
     */
    @Value("${rocketmq.producer.retryTimesWhenSendFailed}")
    private Integer retryTimesWhenSendFailed;

    /**
     * 默认消息配置
     */
    @Bean
    public DefaultMQProducer getRocketMQProducer() throws Exception {
        if (StringUtils.isEmpty(this.normalGroupName)) {
            throw new Exception("no normalGroupName");
        }
        if (StringUtils.isEmpty(this.namesrvAddr)) {
            throw new Exception("nameServerAddr is blank");
        }
        DefaultMQProducer producer;
        producer = new DefaultMQProducer(this.normalGroupName);
        producer.setNamesrvAddr(this.namesrvAddr);
        //如果需要同一个jvm中不同的producer往不同的mq集群发送消息,需要设置不同的instanceName
        //producer.setInstanceName(instanceName);
        if (this.maxMessageSize != null) {
            producer.setMaxMessageSize(this.maxMessageSize);
        }
        if (this.sendMsgTimeout != null) {
            producer.setSendMsgTimeout(this.sendMsgTimeout);
        }
        //如果发送消息失败,设置重试次数,默认为2次
        if (this.retryTimesWhenSendFailed != null) {
            producer.setRetryTimesWhenSendFailed(this.retryTimesWhenSendFailed);
        }

        producer.start();
        log.info(String.format("producer is start ! normalGroupName:[%s],namesrvAddr:[%s]", this.normalGroupName, this.namesrvAddr));
        return producer;
    }

    /**
     * 事务消息配置
     */
    @Bean
    public TransactionMQProducer getTransationRocketMQProducer() throws Exception {
        if (StringUtils.isEmpty(this.transationGroupName)) {
            throw new Exception("no transationGroupName");
        }
        if (StringUtils.isEmpty(this.namesrvAddr)) {
            throw new Exception("nameServerAddr is blank");
        }
        TransactionMQProducer producer = new TransactionMQProducer(this.transationGroupName);
        producer.setNamesrvAddr(this.namesrvAddr);
        MQTransactionListenerProcessor transactionListenerProcessor = new MQTransactionListenerProcessor();
        producer.setTransactionListener(transactionListenerProcessor);
        //如果需要同一个jvm中不同的producer往不同的mq集群发送消息,需要设置不同的instanceName
        //producer.setInstanceName(instanceName);
        if (this.maxMessageSize != null) {
            producer.setMaxMessageSize(this.maxMessageSize);
        }
        if (this.sendMsgTimeout != null) {
            producer.setSendMsgTimeout(this.sendMsgTimeout);
        }
        //如果发送消息失败,设置重试次数,默认为2次
        if (this.retryTimesWhenSendFailed != null) {
            producer.setRetryTimesWhenSendFailed(this.retryTimesWhenSendFailed);
        }
        producer.start();
        log.info(String.format("Transaction producer is start ! transationGroupName:[%s],namesrvAddr:[%s]", this.transationGroupName, this.namesrvAddr));
        return producer;
    }
}

消费者配置


@Slf4j
@SpringBootConfiguration
public class MQConsumerConfiguration {
    @Value("${rocketmq.consumer.namesrvAddr}")
    private String namesrvAddr;
    @Value("${rocketmq.consumer.groupName}")
    private String groupName;
    @Value("${rocketmq.consumer.consumeThreadMin}")
    private int consumeThreadMin;
    @Value("${rocketmq.consumer.consumeThreadMax}")
    private int consumeThreadMax;
    @Value("${rocketmq.consumer.topics}")
    private String topics;
    @Value("${rocketmq.consumer.consumeMessageBatchMaxSize}")
    private int consumeMessageBatchMaxSize;
    @Autowired
    private MQConsumeMsgListenerProcessor mqMessageListenerProcessor;

    @Bean
    public DefaultMQPushConsumer getRocketMQConsumer() throws Exception {
        if (StringUtils.isEmpty(groupName)) {
            throw new Exception("groupName is null");
        }
        if (StringUtils.isEmpty(namesrvAddr)) {
            throw new Exception("namesrvAddr is null");
        }
        if (StringUtils.isEmpty(topics)) {
            throw new Exception("topics is null");
        }
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(groupName);
        consumer.setNamesrvAddr(namesrvAddr);
        consumer.setConsumeThreadMin(consumeThreadMin);
        consumer.setConsumeThreadMax(consumeThreadMax);
        consumer.registerMessageListener(mqMessageListenerProcessor);
        /**
         * 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费
         * 如果非第一次启动,那么按照上次消费的位置继续消费
         */
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
        /**
         * 设置消费模型,集群还是广播,默认为集群
         */
        //consumer.setMessageModel(MessageModel.CLUSTERING);
        /**
         * 设置一次消费消息的条数,默认为1条
         */
   		consumer.setConsumeMessageBatchMaxSize(consumeMessageBatchMaxSize);
        try {
            /**
             * 设置该消费者订阅的主题和tag,如果是订阅该主题下的所有tag,则tag使用*;如果需要指定订阅该主题下的某些tag,则使用||分割,例如tag1||tag2||tag3
             */
            String[] topicTagsArr = topics.split(";");
            for (String topicTags : topicTagsArr) {
                String[] topicTag = topicTags.split("~");
                consumer.subscribe(topicTag[0], topicTag[1]);
            }
            consumer.start();
            log.info("consumer is start !!! groupName:{},topics:{},namesrvAddr:{}", groupName, topics, namesrvAddr);
        } catch (MQClientException e) {
            log.error("consumer is start !!! groupName:{},topics:{},namesrvAddr:{}", groupName, topics, namesrvAddr, e);
            throw new Exception(e);
        }
        return consumer;
    }
}

注入

//注意名字不要变,正好是注入到刚刚的配置中的名字的
@Autowired
private DefaultMQProducer getRocketMQProducer;
@Autowired
private TransactionMQProducer getTransationRocketMQProducer;
可靠同步消息
	/**
     * 可靠同步消息
     */
    public void syn(String tag, String topic, String content) {
        try {
            Message msg = new Message(topic, tag, content.getBytes(RemotingHelper.DEFAULT_CHARSET)
            );
            //Call send message to deliver message to one of brokers.
            SendResult sendResult = defaultMQProducer.send(msg);
            System.out.printf("%s%n", sendResult);
        } catch (MQClientException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (RemotingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (MQBrokerException e) {
            e.printStackTrace();
        }
    }
可靠异步消息

此处有一点问题,后续研究一下再改一下,哪位朋友知道问题的留个言

	/**
     * 可靠异步消息
     */
    public void async(String tag, String topic, String content) {
        defaultMQProducer.setRetryTimesWhenSendAsyncFailed(0);
        try {
            Message msg = new Message(topic, tag, content.getBytes(RemotingHelper.DEFAULT_CHARSET));
            //重点在这里 异步发送回调
            defaultMQProducer.send(msg, new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    System.out.printf("%-10d OK %s %n", sendResult.getMsgId());
                }

                @Override
                public void onException(Throwable e) {
                    System.out.printf("%-10d Exception %s %n", e);
                    e.printStackTrace();
                }
            });
        } catch (MQClientException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (RemotingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
耗时短消息

适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集。
只发送消息,不等待服务器响应,只发送请求不等待应答。此方式发送消息的过程耗时非常短,一般在微秒级别

/**
     * @Author: lǎo xiāng
     * @Date: 2021/2/7 11:17
     * @Describe: 适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集。
     * 只发送消息,不等待服务器响应,只发送请求不等待应答。此方式发送消息的过程耗时非常短,一般在微秒级别
     */
    public void shortSend(String content) {
        try {
            //固定topic和tag
            Message msg = new Message("topic", "TagA", content.getBytes(RemotingHelper.DEFAULT_CHARSET));
            defaultMQProducer.sendOneway(msg);
        } catch (MQClientException e) {
            e.printStackTrace();
        } catch (RemotingException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
延时消息

注意:RocketMQ不支持任意时间的延时,只支持以下几个固定的延时等级 private String messageDelayLevel
= “1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”;

messageDelayLevel可以自己设置级别,在conf/broker.conf里面加入 messageDelayLevel=10s 15s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 3 0m 1h 2h

重启broker: nohup sh mqbroker -n localhost:9876 -c …/conf/broker.conf
&

后测试当级别设为1为10s生效

/**
     *  延时消息
     *  delayTimeLevel为不同级别的时间延时,具体参考配置,可添加自定义
     */
    public void sendDelay(String topic, int delayTimeLevel, String content) {
        Message message = new Message(topic, content.getBytes());
        message.setDelayTimeLevel(delayTimeLevel);
        try {
            SendResult send = defaultMQProducer.send(message);
            log.info("delay message send result:{}", send.toString());
        } catch (MQClientException e) {
            e.printStackTrace();
        } catch (RemotingException e) {
            e.printStackTrace();
        } catch (MQBrokerException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
顺序消息

其中rule为取模根据,比如订单号

/**
     * 顺序消息
     * rule:规则,比如订单号
     */
    public void sendSX(String rule, String topic, String content) {
        Message msg = new Message(topic, content.getBytes());
        SendResult sendResult = null;//订单id
        try {
            sendResult = defaultMQProducer.send(msg, new MessageQueueSelector() {
                @Override
                public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                    //防止订单号规则问题,取hashCode取正
                    int hashCode = arg.hashCode();
                    int index = Math.abs(hashCode) % mqs.size();
                    log.info("orderno:{}, index:{}", arg, index);
                    return mqs.get(index);
                }
            }, rule);
        } catch (MQClientException e) {
            e.printStackTrace();
        } catch (RemotingException e) {
            e.printStackTrace();
        } catch (MQBrokerException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(String.format("SendResult status:%s, queueId:%d, body:%s",
                sendResult.getSendStatus(),
                sendResult.getMessageQueue().getQueueId(),
                content));
    }
事务消息

这个不多说了,一般用于分布式事务解决方案
在这里插入图片描述

    /**
     * 事务消息
     * 这里最好不要单独创建事务管理然后shutdown,否则不会有回查操作,producer在开着的时候才会触发回查操作
     */
    public void transationMsg(String topic, String tag, String content) {
        try {
            //此处的topic可以和其他区分开
            Message msg = new Message(topic, tag, content.getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = getTransationRocketMQProducer.sendMessageInTransaction(msg, null);
            System.out.printf("%s%n", sendResult);
        } catch (MQClientException | UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
public class MQTransactionListenerProcessor implements TransactionListener {

    @Override
    public LocalTransactionState executeLocalTransaction(Message message, Object o) {
        System.out.println("执行本地事务:" + message.getTags());
        if (StringUtils.equals("x", message.getTags())) {
            return LocalTransactionState.COMMIT_MESSAGE;
        } else if (StringUtils.equals("y", message.getTags())) {
            return LocalTransactionState.ROLLBACK_MESSAGE;
        } else{
            return LocalTransactionState.UNKNOW;
        }
    }

    /**
     * TODO 注意回查版本,原来好像是3.2.0没有开源,后来才有的
     */
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
        System.out.println("MQ检查消息Tag【"+messageExt.getTags()+"】的本地事务执行结果:" + DateUtil.now());
        return LocalTransactionState.UNKNOW;
    }
}

都是测试过的,
有什么问题可以私我

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是小酒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值