RocketMQ 发送消息和消费消息

47 阅读3分钟

生产者应用程序

  1. pom.xml:添加 RocketMQ 和 Redisson 依赖。
<dependencies>
    <!-- RocketMQ -->
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-client</artifactId>
        <version>4.9.3</version>
    </dependency>
    <!-- Redisson -->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.16.0</version>
    </dependency>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
</dependencies>
  1. RocketmqProducerManager.java:生产者管理类。
package com.kabai;

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.client.producer.SendStatus;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class RocketmqProducerManager {
    private static final int MAX_RETRY_TIMES = 3; // 最大重试次数
    private DefaultMQProducer producer;

    public RocketmqProducerManager(@Value("${rocketmq.namesrvAddr}") String namesrvAddr,
                                   @Value("${rocketmq.producer.groupName}") String producerGroup) throws MQClientException {
        // 创建一个生产者实例,指定生产者组名
        producer = new DefaultMQProducer(producerGroup);
        producer.setNamesrvAddr(namesrvAddr);
        producer.start(); // 启动生产者实例
    }

    public void sendSuccess(String topic, String tag, String messageBody) {
        Message message = new Message(topic, tag, messageBody.getBytes());
        sendWithRetry(message);
    }

    private void sendWithRetry(Message message) {
        int attempt = 0;
        boolean success = false;

        while (attempt < MAX_RETRY_TIMES && !success) {
            attempt++;
            try {
                SendResult sendResult = producer.send(message);

                // 检查发送状态
                if (sendResult.getSendStatus() == SendStatus.SEND_OK) {
                    System.out.println("Message sent successfully: " + sendResult);
                    success = true;
                } else {
                    System.err.println("Message sending failed: " + sendResult);
                }
            } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
                System.err.println("Message sending failed on attempt " + attempt + ": " + e);
                e.printStackTrace();
            }

            // 如果未成功,等待一段时间后重试
            if (!success) {
                try {
                    Thread.sleep(1000); // 等待1秒后重试
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }

        if (!success) {
            // 发送失败后进行必要的补偿措施,比如记录到数据库、日志或报警
            System.err.println("Failed to send message after " + MAX_RETRY_TIMES + " attempts.");
        }
    }

    public void shutdownProducer() {
        producer.shutdown(); // 关闭生产者实例
    }
}
  1. 生产者的 Spring Boot 应用程序主类
package com.kabai;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RocketmqProducerApplication implements CommandLineRunner {

    @Autowired
    private RocketmqProducerManager rocketmqProducerManager;

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

    @Override
    public void run(String... args) throws Exception {
        rocketmqProducerManager.sendSuccess("TopicTest", "TagA", "Hello RocketMQ, ensuring delivery!");
        rocketmqProducerManager.shutdownProducer();
    }
}
  1. application.properties:配置文件。
rocketmq.namesrvAddr=127.0.0.1:9876
rocketmq.producer.groupName=ReliableProducerGroup

消费者应用程序

  1. pom.xml:添加 RocketMQ 和 Redisson 依赖。
<dependencies>
    <!-- RocketMQ -->
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-client</artifactId>
        <version>4.9.3</version>
    </dependency>
    <!-- Redisson -->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.16.0</version>
    </dependency>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
</dependencies>
  1. RocketmqConsumerManager.java:消费者管理类。
package com.kabai;

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.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class RocketmqConsumerManager {
    private DefaultMQPushConsumer consumer;
    private final RedissonClient redissonClient;

    public RocketmqConsumerManager(RedissonClient redissonClient,
                                   @Value("${rocketmq.namesrvAddr}") String namesrvAddr,
                                   @Value("${rocketmq.consumer.groupName}") String consumerGroup) throws MQClientException {
        this.redissonClient = redissonClient;

        // 创建一个消费者实例
        consumer = new DefaultMQPushConsumer(consumerGroup);
        consumer.setNamesrvAddr(namesrvAddr);
        consumer.start(); // 启动消费者实例
    }

    public void consumeMsg(String topic) throws MQClientException {
        if (consumer == null) {
            throw new IllegalStateException("Consumer is not started. Please start the consumer first.");
        }
        consumer.subscribe(topic, "*");
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    String msgId = msg.getMsgId();
                    RLock lock = redissonClient.getLock("lock:" + msgId);
                    try {
                        // 尝试获取锁
                        if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {
                            try {
                                // 消费消息
                                System.out.println("Received message: " + new String(msg.getBody()));
                            } finally {
                                // 释放锁
                                lock.unlock();
                            }
                        } else {
                            System.out.println("Failed to acquire lock for message: " + msgId);
                        }
                    } catch (Exception e) {
                        System.err.println("Error processing message: " + msgId + ", " + e);
                    }
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
    }

    public void shutdownConsumer() {
        if (consumer != null) {
            consumer.shutdown(); // 关闭消费者实例
        }
    }
}
  1. 消费者的 Spring Boot 应用程序主类
package com.kabai;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RocketmqConsumerApplication implements CommandLineRunner {

    @Autowired
    private RocketmqConsumerManager rocketmqConsumerManager;

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

    @Override
    public void run(String... args) throws Exception {
        // 启动消费者并消费消息
        rocketmqConsumerManager.consumeMsg("TopicTest");

        // 模拟运行一段时间后关闭消费者
        Thread.sleep(5000);
        rocketmqConsumerManager.shutdownConsumer();
    }
}
  1. application.properties:配置文件。
rocketmq.namesrvAddr=127.0.0.1:9876
rocketmq.consumer.groupName=ReliableConsumerGroup
  1. RedissonConfig.java:Redisson 配置类,用于在生产者和消费者应用中共享。
package com.kabai;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Redisson

Config {

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        return Redisson.create(config);
    }
}

关键点

  1. 生产者和消费者分离:生产者和消费者逻辑分别放在不同的 Spring Boot 应用程序中。
  2. 依赖管理:在各自的 pom.xml 中添加 RocketMQ 和 Redisson 的依赖。
  3. 管理类:使用 Spring 的 @Component 注解管理生产者和消费者的逻辑。
  4. Redisson 配置:在两个应用程序中共享 Redisson 配置类。
  5. Spring Boot 主类:在 CommandLineRunner 中实现生产者和消费者的启动逻辑。

通过这种方式,您可以在两个独立的 Spring Boot 应用程序中管理 RocketMQ 的生产者和消费者逻辑,并使用 Redisson 实现分布式锁。