Springboot整合rocketMq

大家好,我是小趴菜,今天整理一下RocketMq这个消息中间件的一些特性,以及使用方式

原生版本

新建一个SpringBoot项目,pom文件如下

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.xpc</groupId>
    <artifactId>springboot-rocketmq</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.4.0</version>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

定义生产者

java 复制代码
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.springframework.stereotype.Component;

@Component
public class XpcProducer {


    private DefaultMQProducer producer;

    /**
     * 定义生产者组
     */
    private final static String PRODUCER_GROUP = "xpc_topic_group";


    public XpcProducer() {
        producer = new DefaultMQProducer();
        producer.setProducerGroup(PRODUCER_GROUP);
        producer.setNamesrvAddr("127.0.0.1:9876");
        try {
            //启动
            producer.start();
        } catch (MQClientException e) {
            e.printStackTrace();
        }
    }

    public DefaultMQProducer getProducer() {
        return this.producer;
    }
}

发送消息

java 复制代码
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class MqController {

    private static final Logger log = LoggerFactory.getLogger(MqController.class);

    @Resource
    private XpcProducer xpcProducer;

    @GetMapping("/sendXpc")
    public String send() throws Exception{
        DefaultMQProducer producer = xpcProducer.getProducer();
        Message message = new Message("xpc-topic-mq","*","","hello mq".getBytes());
        SendResult sendResult = producer.send(message);
        log.info("发送结果是:{}",sendResult.getSendStatus());
        return "ok";
    }
}

定义一个消费者

java 复制代码
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

public class XpcConsumer {

    private static final Logger log = LoggerFactory.getLogger(XpcConsumer.class);

    private static final String CONSUMER_GROUP = "xpc_topic_group_consumer";

    private DefaultMQPushConsumer consumer;

    public XpcConsumer() {
        consumer = new DefaultMQPushConsumer();
        consumer.setNamesrvAddr("127.0.0.1:9876");
        consumer.setConsumerGroup(CONSUMER_GROUP);

        try{
            consumer.subscribe("xpc-topic-mq","*");
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    try{
                        MessageExt messageExt = msgs.get(0);
                        String message = new String(messageExt.getBody());
                        log.info("收到消息:{}",message);
                        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                    }catch (Exception e) {
                        log.error("消费消息异常:{}",e.getMessage());
                        return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                    }
                }
            });
            consumer.start();
        }catch (Exception e) {
            log.error("XpcConsumer | {}",e.getMessage());
        }
    }
}

在启动类中启动消费者

typescript 复制代码
import com.xpc.xpc.XpcConsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
        new XpcConsumer();
    }
}

生产者同步发送

ini 复制代码
Message message = new Message("xpc-topic-mq","*","","hello mq".getBytes());
SendResult sendResult = producer.send(message);
log.info("发送结果是:{}",sendResult.getSendStatus());

生产者异步发送

typescript 复制代码
Message message = new Message("xpc-topic-mq","*","","hello mq".getBytes());
producer.send(message, new SendCallback() {
    @Override
    public void onSuccess(SendResult sendResult) {
        log.info("发送结果是:{}",sendResult.getSendStatus());
    }
    @Override
    public void onException(Throwable e) {
        log.info("发送消息异常: {}",e.getMessage());
    }
});

生产者ONEWAY发送方式

ini 复制代码
Message message = new Message("xpc-topic-mq","*","","hello mq".getBytes());
producer.sendOneway(message);

延迟消息

java 复制代码
@GetMapping("/delayMsg")
public String delayMsg() throws Exception{
    DefaultMQProducer producer = xpcProducer.getProducer();
    Message message = new Message("xpc-topic-mq","*","","这是延时消息".getBytes());
    //延时消息:1s,5s,10s,30s,1m,2m,3m,.....1h,2h
    //定义消息延时级别
    message.setDelayTimeLevel(2);
    SendResult sendResult = producer.send(message);
    log.info("消息发送结果:{}",sendResult.getSendStatus());
    return "ok";
}

从发送成功到消息中间延迟了5s

顺序消息

生产者

java 复制代码
@GetMapping("/orderLy")
public String orderLy() throws Exception{
    DefaultMQProducer producer = xpcProducer.getProducer();
    for(int i = 0; i < 5; i ++) {
        Message message = new Message("xpc-topic-mq-orderly","*","","这是顺序消息-".getBytes());
        producer.send(message, new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                //arg的值就是send方法最后一个参数,也就是我们这里传的1
                int parseInt = Integer.parseInt(arg.toString());
                int index = parseInt % mqs.size();
                return mqs.get(index);
            }
        },1);
    }
    return "ok";
}

消费者

java 复制代码
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

public class MqOrderlyConsumer {

    private static final Logger log = LoggerFactory.getLogger(MqOrderlyConsumer.class);

    private static final String CONSUMER_GROUP = "xpc_topic_group_consumer_orderly";


    private DefaultMQPushConsumer consumer;

    public MqOrderlyConsumer() {
        consumer = new DefaultMQPushConsumer();
        consumer.setNamesrvAddr("127.0.0.1:9876");
        consumer.setConsumerGroup(CONSUMER_GROUP);

        try {
            consumer.subscribe("xpc-topic-mq-orderly","*");
            
            //这里要切换成new MessageListenerOrderly()
            consumer.registerMessageListener(new MessageListenerOrderly() {
                @Override
                public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                    MessageExt messageExt = msgs.get(0);
                    try{
                        log.info("收到服务端消息:{}",new String(messageExt.getBody()));
                        return ConsumeOrderlyStatus.SUCCESS;
                    }catch (Exception e) {
                        return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
                    }
                }
            });
            consumer.start();
        } catch (MQClientException e) {
            e.printStackTrace();
        }
    }
}

-- 注意:顺序消息只能同步发送,不能使用异步发送,不然消费者顺序会不一致

SpringBoot整合RocketMQ

一般我们很少使用上面这种方式来使用,比较常用的是接下来这种,新建一个SpringBoot项目,pom.xml文件如下

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xpc</groupId>
    <artifactId>springboot-rocketmq</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

application.yml文件如下

yaml 复制代码
rocketmq:
  consumer:
    group: xpc_consumer_group
    pull-batch-size: 10

  producer:
    group: xpc_producer_group
    send-message-timeout: 3000
    retry-times-when-send-failed: 2
    max-message-size: 4096
    retry-next-server: false
  name-server: 127.0.0.1:9876

server:
  port: 7002

生产者

typescript 复制代码
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.UUID;

@Slf4j
@RestController
public class ProducerController {


    @Resource
    private RocketMQTemplate rocketMQTemplate;


    /**
     * 同步发送
     * @return
     */
    @GetMapping("/send")
    public String send() {
        Message<String> msg = MessageBuilder.withPayload("Hello,RocketMQ").build();
        SendResult sendResult = rocketMQTemplate.syncSend("xpc-topic", msg);
        log.info("消息发送结果:{}",sendResult.getSendStatus());
        return "ok";
    }

    /**
     * 异步发送
     */
    @GetMapping("/asyncSend")
    public String asyncSend() {
        Message<String> msg = MessageBuilder.withPayload("这是异步发送").build();
        rocketMQTemplate.asyncSend("xpc-topic", msg, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("异步发送结果:{}",sendResult.getSendStatus());
            }

            @Override
            public void onException(Throwable throwable) {
                log.info("异步发送异常:{}",throwable.getMessage());
            }
        });
        return "ok";
    }

    /**
     * 单向发送
     */
    @GetMapping("/oneWay")
    public String oneWay() {
        Message<String> msg = MessageBuilder.withPayload("这是单向发送").build();
        rocketMQTemplate.sendOneWay("xpc-topic",msg);
        return "ok";
    }

    /**
     * 顺序消息
     */
    @GetMapping("/orderly")
    public String orderly() {
        Message<String> msg = MessageBuilder.withPayload("这是顺序消息").build();
        //最后一个参数会被使用决定使用topic中的哪个queue,默认使用的Hash取模SelectMessageQueueByHash
        SendResult sendResult = rocketMQTemplate.syncSendOrderly("xpc-topic-orderly", msg, "1");
        return "ok";
    }

    /**
     * 事务消息
     */
    @GetMapping("/tx")
    public String tx() {
        String uuid = UUID.randomUUID().toString();
        Message<String> msg = MessageBuilder
                .withPayload("这是事务消息")
                .setHeader(RocketMQHeaders.TRANSACTION_ID,uuid)
                .setHeader(RocketMQHeaders.KEYS,"1001")
                .build();
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction("xpc-topic-tx", msg, null);
        log.info("事务消息发送结果:{}",transactionSendResult.getSendStatus());
        return "ok";
    }
}

定义消费者

typescript 复制代码
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RocketMQMessageListener(
        topic = "xpc-topic-tx",
        consumerGroup = "xpc_consumer_group-tx"
        )
public class SyncConsumer implements RocketMQListener<MessageExt> , RocketMQPushConsumerLifecycleListener {

    @Override
    public void onMessage(MessageExt message) {
        byte[] body = message.getBody();
        log.info("消费者接收到消息:{}",new String(body));

        try{
            int i = 1 / 0;
        }catch (Exception e) {
            //如果达到重试次数,执行后续的业务逻辑
            log.error("重试次数:{}",message.getReconsumeTimes());
            throw new RuntimeException("测试重试次数");
        }
    }

    @Override
    public void prepareStart(DefaultMQPushConsumer consumer) {
        try {
            //设置消费端重试次数
            consumer.setMaxReconsumeTimes(2);
            consumer.subscribe("xpc-topic-tx","*");
        } catch (MQClientException e) {
            e.printStackTrace();
        }
    }
}

顺序消息消费者

typescript 复制代码
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RocketMQMessageListener(
        consumerGroup = "orderly-group",
        //主要是把该值设置为ConsumeMode.ORDERLY
        consumeMode = ConsumeMode.ORDERLY,
        topic = "xpc-topic-orderly",
        messageModel = MessageModel.CLUSTERING
)
public class OrderlyConsumer implements RocketMQListener {


    @Override
    public void onMessage(Object message) {
        log.info("收到的消息是:{}",message);
    }
}

事务消息的业务代码需要写在Listener中,如下

typescript 复制代码
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;


@Slf4j
@Component
@RocketMQTransactionListener(rocketMQTemplateBeanName = "rocketMQTemplate")
public class TxConsumer implements RocketMQLocalTransactionListener {

    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        //执行本地事务
        String payload = (String) msg.getPayload();
        log.info("收到消息:{}",payload);
        return RocketMQLocalTransactionState.UNKNOWN;
    }

    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        log.info("开始执行本地回查");
        //这里的值在发送事务消息指定的两个Header的值
        Object txId = msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID);
        Object key = msg.getHeaders().get(RocketMQHeaders.KEYS);
        return RocketMQLocalTransactionState.COMMIT;
    }
}
相关推荐
qq_327342732 分钟前
Java实现离线身份证号码OCR识别
java·开发语言
锅包肉的九珍3 分钟前
Scala的Array数组
开发语言·后端·scala
心仪悦悦6 分钟前
Scala的Array(2)
开发语言·后端·scala
2401_8827275738 分钟前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
心仪悦悦1 小时前
Scala中的集合复习(1)
开发语言·后端·scala
阿龟在奔跑1 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF1 小时前
m个数 生成n个数的所有组合 详解
java·递归
代码小鑫2 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖2 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶2 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka