使用RocketMQTemplate发送带tags的消息

RocketMQTemplate发送带tags的消息

RocketMQTemplate是RocketMQ集成到Spring cloud之后提供的个方便发送消息的模板类,它是基本Spring 的消息机制实现的,对外只提供了Spring抽象出来的消息发送接口。

在单独使用RocketMQ的时候,发送消息使用的Message是‘org.apache.rocketmq.common.message'包下面的Message,而使用RocketMQTemplate发送消息时,使用的Message是org.springframework.messaging的Message,猛一看,没办法发送带tags的消息了,其实在RocketMQ集成的时候已经解决了这个问题。

在RocketMQTemplate发送消息时,调用的方法是:

public SendResult syncSendOrderly(String destination, Message<?> message, String hashKey, long timeout) {
        if (Objects.isNull(message) || Objects.isNull(message.getPayload())) {
            log.error("syncSendOrderly failed. destination:{}, message is null ", destination);
            throw new IllegalArgumentException("`message` and `message.payload` cannot be null");
        }
        try {
            long now = System.currentTimeMillis();
          //在这里对消息进行了转化,将Spring的message转化为rocketmq自己的message
            org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper,
                charset, destination, message);
            SendResult sendResult = producer.send(rocketMsg, messageQueueSelector, hashKey, timeout);
            long costTime = System.currentTimeMillis() - now;
            log.debug("send message cost: {} ms, msgId:{}", costTime, sendResult.getMsgId());
            return sendResult;
        } catch (Exception e) {
            log.error("syncSendOrderly failed. destination:{}, message:{} ", destination, message);
            throw new MessagingException(e.getMessage(), e);
        }
    }

在上面的代码中,对消息进行了转化,将Spring的message转化为rocketmq自己的message,在RocketMQUtil.convertToRocketMessage方法中有个地方就是获取tags的:

        String[] tempArr = destination.split(":", 2);
        String topic = tempArr[0];
        String tags = "";
        if (tempArr.length > 1) {
            tags = tempArr[1];
        }

所以,在发送消息的时候,我们只要把tags使用":"添加到topic后面就可以了。

例如:xxxx:tag1 || tag2 || tag3

使用RocketMQ 处理消息

消息发送(生产者)

以maven + SpringBoot 工程为例,先在pom.xml增加依赖

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.0.1</version>
</dependency>

由于,这个依赖是一个starter,直接引入依赖就可以开始写投递消息的代码了。这个starter注册了一个叫org.apache.rocketmq.spring.core.RocketMQTemplate的bean,用它就可以直接把消息投递出去。 具体的API是这样的

    XXXEvent xxxDto = new XXXEvent();
    Message<XXXEvent> message = MessageBuilder.withPayload(xxxDto).build();
    String dest = String.format("%s:%s",topic-name","tag-name");
    //默认投递:同步发送 不会丢失消息。如果在投递成功后发生网络异常,客户端会认为投递失败而回滚本地事务
    this.rocketMQTemplate.send(dest, xxxDto);

这种投递方式能保证投递成功的消息不会丢失,但是不能保证投递一定成功。假设一次调用的流程是这样的

如果在步骤3的时候发生错误,因为出错mqClient会认为消息投递失败而把事务回滚。如果消息已经被消费,那就会导致业务错误。我们可以用事务消息解决这个问题。

以带事务方式投递的消息,正常情况下的处理流程是这样的

出错的时候是这样的

由于普通消息没有消息回查,普通消息用的producer不支持回查操作,不同业务的回查处理也不一样,事务消息需要使用单独的producer。消息发送代码大概是这样的

//调用这段代码之前别做会影响数据的操作
XXXEvent xxxDto = new XXXEvent();
Message<XXXEvent> message = MessageBuilder.withPayload(xxxDto).build();
String dest = String.format("%s:%s",topic-name","tag-name");
TransactionSendResult transactionSendResult = this.rocketMQTemplate.sendMessageInTransaction("poducer-name","topic-name:tag-name",message,"xxxid");
if (LocalTransactionState.ROLLBACK_MESSAGE.equals(transactionSendResult.getLocalTransactionState())){
    throw new RuntimeException("事务消息投递失败");
}
//按照RocketMQ的写法,这个地方不应该有别的代码
@RocketMQTransactionListener(txProducerGroup = "producer")
    class TransactionListenerImpl implements RocketMQLocalTransactionListener {

        //消息投递成功后执行的逻辑(半消息)
        //原文:When send transactional prepare(half) message succeed, this method will be invoked to execute local transaction.
        @Override
        public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
            try{
                //
                xxxService.doSomething();
                return RocketMQLocalTransactionState.COMMIT;
            catch(IOException e){
                //不确定最终是否成功
                return RocketMQLocalTransactionState.UNKNOWN;
            }catch(Exception e){
                return RocketMQLocalTransactionState.ROLLBACK;
            }
        }
        //回查事务执行状态
        @Override
        public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
            Boolean result = xxxService.isSuccess(msg,arg);
            if(result != null){
                if(result){
                    return RocketMQLocalTransactionState.COMMIT;
                }else{
                    return RocketMQLocalTransactionState.ROLLBACK;
                }
            }
            return RocketMQLocalTransactionState.UNKNOWN;
        }
    }

处理消息(消费)

普通消息和事务消息的区别只在投递的时候才明显,对应的消费端代码比较简单

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RocketMQMessageListener(consumerGroup = "xxx-consumer", topic = "topic-name",selectorExpression = "tag-name")
public class XXXEventMQListener implements RocketMQListener<XXXEvent> {
    private  String repeatCheckRedisKeyTemplate = "topic-name:tag:repeat-check:%s";
    @Autowired private StringRedisTemplate redisTemplate;
    @Override
    public void onMessage(XXXEvent message) {
        log.info("consumer message {}",message);
        //处理消息
        try{
            xxxService.doSomething(message);
        }catch(Exception ex){
            log.warn(String.format("message [%s] 消费失败",message),ex);
            //抛出异常后,MQClient会返回ConsumeConcurrentlyStatus.RECONSUME_LATER,这条消息会再次尝试消费
            throw new RuntimException(ex);
        }
    }
}

RocketMQ用ACK机制保证NameServer知道消息是否被消费在

org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer里是这么处理的

public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently {
    @SuppressWarnings("unchecked")
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        for (MessageExt messageExt : msgs) {
            log.debug("received msg: {}", messageExt);
            try {
                long now = System.currentTimeMillis();
                rocketMQListener.onMessage(doConvertMessage(messageExt));
                long costTime = System.currentTimeMillis() - now;
                log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime);
            } catch (Exception e) {
                log.warn("consume message failed. messageExt:{}", messageExt, e);
                context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume);
                return ConsumeConcurrentlyStatus.RECONSUME_LATER;
            }
        }
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 浅谈Springboot整合RocketMQ使用心得

    一.阿里云官网---帮助文档 https://help.aliyun.com/document_detail/29536.html?spm=5176.doc29535.6.555.WWTIUh 按照官网步骤,创建Topic.申请发布(生产者).申请订阅(消费者) 二.代码 1.配置: public class MqConfig { /** * 启动测试之前请替换如下 XXX 为您的配置 */ public static final String PUBLIC_TOPIC = "test"

  • Springboot RocketMq实现过程详解

    首先,在虚拟机上安装rocketmq和rocketMq可视化控制,安装不做描述. 1.pom.xml文件添加依赖 mq的版本与连接的rocketmq版本保持一致 <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-remoting</artifactId> <version>4.4.0</version> </depende

  • java rocketmq--消息的产生(普通消息)

    前言 与消息发送紧密相关的几行代码: 1. DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName"); 2. producer.start(); 3. Message msg = new Message(...) 4. SendResult sendResult = producer.send(msg); 5. producer.shutdown(); 那这几行代码执行时,背后都做了什么? 一. 首先

  • RocketMq事务消息发送代码流程详解

    一.RocketMq事务消息流程: 1.首先会向broker发送一个预请求消息,消费者不可见 2.回调执行本地事务(比如操作数据库) 3.事务执行成功后,再次发送消息给broker,告诉broker事务执行成功这个消息要提交,让消费者可见.如果本地事务执行超时,会返回一个unknow,broker会发送一个消息回查,检查消息是否执行成功. 二.RocketMq事务消息实例: 1.引入rocketMq相关的依赖: <dependency> <groupId>org.apache.ro

  • 使用RocketMQTemplate发送带tags的消息

    RocketMQTemplate发送带tags的消息 RocketMQTemplate是RocketMQ集成到Spring cloud之后提供的个方便发送消息的模板类,它是基本Spring 的消息机制实现的,对外只提供了Spring抽象出来的消息发送接口. 在单独使用RocketMQ的时候,发送消息使用的Message是'org.apache.rocketmq.common.message'包下面的Message,而使用RocketMQTemplate发送消息时,使用的Message是org.s

  • 用php发送带附件的Email

    参考了一下网上的文章.俗话说,天下文章一大抄,看你会抄不会抄.关键是能为我所用,这是最重要的.废话不多讲,let's go. 其实发mail很简单,php有现成的函数,可以参考php 的 manual,特别是第四个例子,讲的很详细. 关键是怎么把上传附件跟邮件发送结合起来.关于文件的上传,可以参考http://blog.csdn.net/slamdunk3/archive/2005/02/23/299025.aspx 这篇文章. 讲一下 文件上传的方法及其属性: 我们假设文件上传字段的名称如上例

  • python3.8 微信发送服务器监控报警消息代码实现

    这篇文章主要介绍了python3.8 微信发送服务器监控报警消息代码实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 python版本 >>> import sys >>> sys.version '3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)]' 代码 urllib是python3.8内置的 #!/usr/

  • Python3使用SMTP发送带附件邮件

    一.设置开启SMTP服务并获取授权码 可以参考第一篇文章,这里不再赘述:[一]https://www.jb51.net/article/142220.htm 二.使用Python3 发送带附件的邮件 0.使用的环境为: Python 3.6.3 (64bit) PyCharm 2017.3 (64bit) 1.注意事项: 在调用smtplib,发送简单邮件的时候使用smtplib.SMTP()方法使没有问题的. 但是如果是邮件存在附件的情况下,还使用smtplib.SMTP()方法,就可能会发送

  • python 实现Requests发送带cookies的请求

    一.缘 起 最近学习[悠悠课堂]的接口自动化教程,文中提到Requests发送带cookies请求的方法,笔者随之也将其用于手头实际项目中,大致如下 二.背 景 实际需求是监控平台侧下发消息有无异常,如有异常便触发报警推送邮件,项目中下发消息接口需要带cookies 三.说 明 脚本的工程名为ynJxhdSendMsg,大致结构如下图 sendMsg.py为主程序,函数checkMsg为在已发消息列表中查找已下发消息,函数sendMsg为发消息并根据结果返回对应的标识 sendAlertEmai

  • 如何用ASP发送带附件的邮件?

    如何用ASP发送带附件的邮件?<% Option Explicit Dim objMail Set objMail = Server.CreateObject("CDONTS.NewMail") objMail.From = "liyanbing@163.net " objMail.Subject = "闪亮日子之星河影动" objMail.AttachFile("s:\flashdays\images\win.jpg"

  • 用vbs发送带附件的邮件

    function Send_mail(You_Account,You_Password,Send_Email,Send_Email2,Send_Topic,Send_Body,Send_Attachment)  'code by NetPatch 'VBS发送邮件参数说明 'You_Account:你的邮件帐号 'You_Password:你的邮件密码 'Send_Email: 主要邮件地址 'Send_Email2: 备用邮件地址 'Send_Topic: 邮件主题 'Send_Body:  

  • java中javamail发送带附件的邮件实现方法

    本文实例讲述了java中javamail发送带附件的邮件实现方法.分享给大家供大家参考.具体分析如下: JavaMail,顾名思义,提供给开发者处理电子邮件相关的编程接口.它是Sun发布的用来处理email的API.它可以方便地执行一些常用的邮件传输,JavaMail是可选包,因此如果需要使用的话你需要首先从java官网上下载.目前最新版本是JavaMail1.5.0,下面我们来看看javamail发送带附件的邮件实例 mail.java 代码: 复制代码 代码如下: package mail;

  • Java发送带html标签内容的邮件实例代码

    如下所示: package test; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; import javax.mail.Session; import javax.mail.MessagingException; import javax.mail.Transport; public class

  • smtp发送带附件的邮件代码分享

    该方式直接保存为HTML文件,也可以是文本文件,其它格式效果不是很好 复制代码 代码如下: MailMessage mmsg = new MailMessage();     mmsg.Subject = "邮件标题";     mmsg.Body = "邮件内容";     mmsg.To.Add("accept@qq.com");//接收邮箱     byte[] bytes = System.Text.Encoding.Default.Ge

随机推荐