为了账号安全,请及时绑定邮箱和手机立即绑定

手写rocketMQ项目实战:新手入门教程

标签:
Java 中间件
概述

本文详细介绍了RocketMQ的基本概念、安装步骤以及消息模型,并通过手写RocketMQ项目实战,深入讲解了生产者与消费者的基本用法,包括消息的发送、接收和高级特性,帮助读者全面掌握RocketMQ的使用方法。手写RocketMQ项目实战涵盖了从创建生产者和消费者到发送和接收消息的全过程,同时提供了详细的代码示例和实战案例。

RocketMQ简介与安装
RocketMQ是什么

RocketMQ是由阿里云开源的一个高性能分布式消息中间件,广泛应用于阿里巴巴集团内部的业务场景以及外部企业。RocketMQ主要用于解耦系统设计中的松耦合架构,允许系统之间异步通信,从而提高系统的可扩展性和稳定性。RocketMQ具有高并发、低延迟、高可用等特点,支持多种消息模型,可以满足不同场景下的需求。

RocketMQ的特点与优势
  1. 高性能:RocketMQ采用了一系列高性能设计,如零拷贝技术、大内存页支持等,确保高吞吐量和低延迟。
  2. 高可用性:RocketMQ通过多Master多Slave集群模式实现消息的高可用性,避免单点故障。
  3. 消息可靠性:RocketMQ支持事务消息和顺序消息,确保消息的可靠传输。
  4. 灵活的消息路由:RocketMQ支持多种路由策略,如广播、集群等,可以根据业务需求灵活配置。
  5. 消息过滤与重试:RocketMQ支持多种消息过滤策略,如Tag、SQL过滤等,同时提供消息重试机制,确保消息的正确处理。
  6. 监控与管理:RocketMQ提供了完善的监控体系,可以实时监控集群状态,方便运维人员进行故障排查。
RocketMQ的安装步骤
  1. 下载RocketMQ:访问RocketMQ的GitHub仓库,下载最新版本的RocketMQ。

  2. 解压RocketMQ

    tar -xvf rocketmq-all-4.9.2-bin-release.zip
    cd rocketmq-all-4.9.2-bin-release
  3. 安装JDK:确保系统已经安装了Java JDK环境,并配置好环境变量。假设JDK版本为1.8,可以使用以下命令进行安装:

    sudo apt-get update
    sudo apt-get install openjdk-8-jdk
  4. 启动NameServer:NameServer是RocketMQ集群中的消息路由中心,用于存储和管理Broker的信息。

    nohup sh bin/mqnamesrv &
  5. 启动Broker:Broker是RocketMQ集群中的消息存储中心,负责接收和存储生产者发送的消息,并将消息推送给消费者。

    nohup sh bin/mqbroker -n 127.0.0.1:9876 &
  6. 验证安装:可以通过浏览器访问http://localhost:9876/来查看NameServer的状态,如果显示正常,说明RocketMQ已经安装成功。
RocketMQ的核心概念
消息模型

RocketMQ支持多种消息模型,包括单向消息、同步消息和异步消息。单向消息模型中,生产者发送消息后不等待响应;同步消息模型中,生产者发送消息后等待响应,只有在收到确认消息后才认为消息发送成功;异步消息模型中,生产者发送消息后通过回调函数异步接收确认消息。

名称服务器(Naming Server)

名称服务器(Name Server)是RocketMQ集群中的消息路由中心,用于存储和管理Broker的信息。当生产者或消费者启动时,它们会连接到Name Server,获取Broker的地址信息,从而实现消息的路由。Name Server提供了一个简单的HTTP接口用于获取Broker的信息,生产者和消费者可以通过该接口获取到需要连接的Broker地址。

消息队列(Broker)

消息队列(Broker)是RocketMQ集群中的消息存储中心,负责接收和存储生产者发送的消息,并将消息推送给消费者。在RocketMQ中,Broker分为两种类型:Master和Slave。Master负责接收消息并将其存储到本地文件系统中,Slave则从Master同步数据,实现数据的冗余备份。在集群模式下,Master负责向Slave同步消息,实现消息的高可用性。

消费者(Consumer)

消费者(Consumer)是消息的接收方,负责从Broker中接收并处理消息。RocketMQ支持两种类型的消费者:集群模式(Cluster)和广播模式(Broadcast)。集群模式下,消息只会被集群中的一个消费者处理,保证消息的正确性;广播模式下,消息会被所有消费者处理,保证消息的可靠性。

生产者(Producer)

生产者(Producer)是消息的发送方,负责将消息发送到Broker中。RocketMQ支持多种类型的消息发送策略,包括同步发送(Sync)、异步发送(Async)和单向发送(OneWay)。同步发送模式下,生产者发送消息后等待响应,只有在收到确认消息后才认为消息发送成功;异步发送模式下,生产者发送消息后通过回调函数异步接收确认消息;单向发送模式下,生产者发送消息后不等待响应,直接返回。

手写RocketMQ生产者与消费者
创建生产者

RocketMQ生产者的主要职责是创建消息并将其发送到指定的Topic。首先,生产者需要创建一个DefaultMQProducer实例,设置Producer Group名称,然后调用Start方法启动生产者。

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;

public class Producer {
    public static void main(String[] args) throws Exception {
        // 创建一个生产者实例
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        // 设置Name Server地址
        producer.setNamesrvAddr("localhost:9876");
        // 启动生产者
        producer.start();
        // 创建一个消息
        Message msg = new Message("TopicTest", // Topic
                "TagA", // Tag
                ("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET), // body
                1000); // timeout
        // 发送消息
        producer.send(msg);
        // 关闭生产者
        producer.shutdown();
    }
}

创建生产者的主要步骤如下:

  1. 创建DefaultMQProducer实例。
  2. 设置Producer Group名称。
  3. 设置Name Server地址。
  4. 启动生产者。
  5. 创建消息,并设置消息的Topic、Tag、Body和Timeout。
  6. 发送消息。
  7. 关闭生产者。
创建消费者

RocketMQ消费者的职责是接收并处理来自指定Topic的消息。首先,消费者需要创建一个DefaultMQPushConsumer实例,设置Consumer Group名称,并订阅相应Topic的主题。然后调用Start方法启动消费者。

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.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;

public class Consumer {
    public static void main(String[] args) throws Exception {
        // 创建一个消费者实例
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
        // 设置Name Server地址
        consumer.setNamesrvAddr("localhost:9876");
        // 订阅主题
        consumer.subscribe("TopicTest", "*");
        // 设置消费模式为顺序消费
        consumer.setMessageModel(MessageModel.BROADCASTING);
        // 设置从队列头部开始消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        // 注册消息监听器
        consumer.registerMessageListener((MessageListenerOrderly) (msgs, context) -> {
            for (MessageExt msg : msgs) {
                System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), new String(msg.getBody()));
            }
            // 返回消费状态
            return ConsumeOrderlyStatus.SUCCESS;
        });
        // 启动消费者
        consumer.start();
    }
}

创建消费者的主要步骤如下:

  1. 创建DefaultMQPushConsumer实例。
  2. 设置Consumer Group名称。
  3. 设置Name Server地址。
  4. 订阅主题。
  5. 设置消费模式。
  6. 设置从队列头部开始消费。
  7. 注册消息监听器。
  8. 启动消费者。
发送与接收消息的基本流程

生产者和消费者启动后,生产者可以开始发送消息,消费者可以接收并处理消息。生产者发送的消息会被Broker存储,当消费者连接到Broker时,Broker会根据消息的路由信息将消息推送给消费者。消费者接收到消息后,会调用消息监听器处理消息。

消息发送与接收的高级特性
消息的顺序发送与接收

RocketMQ支持顺序消息的发送与接收,可以确保消息按照一定的顺序被生产和消费。通过设置消息的键(Key)来指定消息的顺序,并设置消息消费模式为ORDERLY来实现顺序消费。以下是一个简单的示例:

public class Producer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        producer.setNamesrvAddr("localhost:9876");
        producer.start();
        for (int i = 0; i < 10; i++) {
            Message msg = new Message("TopicTest", // Topic
                    "TagA", // Tag
                    ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET), // body
                    i); // key
            producer.send(msg);
        }
        producer.shutdown();
    }
}

public class Consumer {
    public static void main(String[] args) throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
        consumer.setNamesrvAddr("localhost:9876");
        consumer.subscribe("TopicTest", "*");
        consumer.setMessageModel(MessageModel.ORDERLY);
        consumer.registerMessageListener((MessageListenerOrderly) (msgs, context) -> {
            for (MessageExt msg : msgs) {
                System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), new String(msg.getBody()));
            }
            return ConsumeOrderlyStatus.SUCCESS;
        });
        consumer.start();
    }
}

顺序发送与接收适用于需要保证消息顺序的场景,例如订单处理流程。

消息的延迟发送

RocketMQ支持延迟消息的发送,可以通过设置消息的延迟级别来实现。RocketMQ提供了7个级别的延迟,分别是1秒、5秒、10秒、30秒、1分钟、2分钟、3分钟。在发送消息时,可以通过设置Message的DelayTimeLevel属性来指定延迟时间。

public class Producer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        producer.setNamesrvAddr("localhost:9876");
        producer.start();
        Message msg = new Message("TopicTest", // Topic
                "TagA", // Tag
                ("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET), // body
                1000); // timeout
        msg.setDelayTimeLevel(1); // 设置延迟级别为1(1秒)
        producer.send(msg);
        producer.shutdown();
    }
}

延迟发送适用于需要延迟一定时间后处理消息的场景,例如定时任务或者定时通知。

消息的事务处理

RocketMQ支持事务消息的发送与接收,可以确保消息的一致性。在发送事务消息时,生产者需要先将消息发送到Broker,然后执行本地事务。如果本地事务执行成功,则向Broker发送确认消息,Broker会将消息推送给消费者;如果本地事务执行失败,则向Broker发送回滚消息,Broker不会将消息推送给消费者。

import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.TransactionSendResult;

public class Producer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        producer.setNamesrvAddr("localhost:9876");
        producer.setTransactionCheckListener(new TransactionCheckListener() {
            @Override
            public LocalTransactionState checkLocalTranscationState(String tranMsgId) {
                // 根据tranMsgId查询本地事务状态
                return LocalTransactionState.COMMIT_MESSAGE;
            }
        });
        producer.start();
        TransactionMQProducerImpl transactionMQProducer = (TransactionMQProducerImpl) producer;
        transactionMQProducer.setLocalTransactionExecuter(new LocalTransactionExecuter() {
            @Override
            public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
                // 执行本地事务
                return LocalTransactionState.UNKNOW;
            }
        });
        TransactionSendResult result = producer.send(new Message("TopicTest", // Topic
                "TagA", // Tag
                ("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET), // body
                1000)); // timeout
        System.out.printf("%s %s %n", result.getLocalTransactionState(), result.getMsgId());
        producer.shutdown();
    }
}

事务处理适用于需要确保消息可靠传输的场景,例如金融交易或其他重要的业务逻辑。

异常处理与故障排除
常见错误及其解决方案
  1. 无法连接到Name Server:检查Name Server是否启动,Name Server地址是否正确。
  2. 无法连接到Broker:检查Broker是否启动,Broker地址是否正确。
  3. 消息发送失败:检查生产者是否配置正确,消息体是否为空。
  4. 消息接收失败:检查消费者是否配置正确,消费者组是否冲突。
  5. 消息丢失:检查Broker是否正常运行,是否配置了消息重试机制。
  6. 消息重复:检查生产者和消费者的消费模式是否一致,是否配置了消息去重机制。
日志分析与监控

RocketMQ提供了丰富的日志和监控功能,可以帮助运维人员及时发现和处理问题。RocketMQ的日志主要包括Name Server日志、Broker日志和Client日志。Name Server日志记录了Name Server的启动、停止和运行状态;Broker日志记录了Broker的启动、停止和运行状态,以及消息的接收、存储和推送状态;Client日志记录了生产者和消费者的启动、停止和运行状态,以及消息的发送和接收状态。

// 查看Name Server日志
tail -f ~/rocketmq/logs/NameServer/log.2023-01-01
// 查看Broker日志
tail -f ~/rocketmq/logs/Broker/log.2023-01-01
// 查看Client日志
tail -f ~/rocketmq/logs/Client/log.2023-01-01

RocketMQ提供了Rocketmq-console工具,可以帮助运维人员实时监控RocketMQ集群的状态。Rocketmq-console是一个Web应用,可以通过浏览器访问Rocketmq-console的Web界面,查看RocketMQ集群的运行状态、性能指标和报警信息。

// 启动Rocketmq-console
nohup java -jar rocketmq-console.jar --server.port=8080 --rocketmq.config.namesrvAddr=localhost:9876 &
// 访问Rocketmq-console
http://localhost:8080
消息丢失与重复处理

RocketMQ支持消息重试机制,可以在生产者和消费者端配置消息重试次数和重试间隔时间,确保消息的可靠传输。消息重试机制可以有效地解决消息丢失和重复的问题。

public class Producer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        producer.setNamesrvAddr("localhost:9876");
        producer.setRetryTimesWhenSendFailed(5); // 设置重试次数为5
        producer.setSendMsgTimeout(30000); // 设置发送超时时间为30秒
        producer.start();
        Message msg = new Message("TopicTest", // Topic
                "TagA", // Tag
                ("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET), // body
                1000); // timeout
        producer.send(msg);
        producer.shutdown();
    }
}
实战案例
实战项目的设计思路

本实战项目的目标是实现一个简单的订单系统,使用RocketMQ作为消息中间件,实现订单创建和支付通知的功能。订单创建和支付通知是两个异步操作,通过RocketMQ的消息中间件可以实现这两个操作的解耦,提高系统的扩展性和稳定性。

设计思路如下:

  1. 订单创建:用户提交订单后,订单服务会创建一个订单,并将订单信息发送到RocketMQ的订单创建Topic。
  2. 支付通知:支付服务会订阅订单创建Topic,接收到订单创建消息后,会向RocketMQ的支付通知Topic发送支付通知消息。
  3. 订单支付:支付服务会订阅支付通知Topic,接收到支付通知消息后,会向订单服务发送支付成功的消息。
  4. 订单完成:订单服务会订阅支付成功的Topic,接收到支付成功的消息后,会更新订单状态为支付完成。
实战项目的编码实现
// 订单创建服务
public class OrderService {
    public void createOrder(String orderId) throws Exception {
        // 创建订单
        // 发送订单创建消息
        Message msg = new Message("OrderCreateTopic", // Topic
                "TagA", // Tag
                ("Order Id: " + orderId).getBytes(RemotingHelper.DEFAULT_CHARSET), // body
                1000); // timeout
        producer.send(msg);
    }
}

// 支付服务
public class PaymentService {
    public void onOrderCreated(String orderId) throws Exception {
        // 发送支付通知消息
        Message msg = new Message("PaymentNotifyTopic", // Topic
                "TagA", // Tag
                ("Order Id: " + orderId).getBytes(RemotingHelper.DEFAULT_CHARSET), // body
                1000); // timeout
        producer.send(msg);
    }

    public void onPaymentSuccess(String orderId) throws Exception {
        // 发送支付成功消息
        Message msg = new Message("PaymentSuccessTopic", // Topic
                "TagA", // Tag
                ("Order Id: " + orderId).getBytes(RemotingHelper.DEFAULT_CHARSET), // body
                1000); // timeout
        producer.send(msg);
    }
}

// 订单服务
public class OrderService {
    public void onPaymentSuccess(String orderId) throws Exception {
        // 更新订单状态为支付完成
    }
}
实战项目的部署与运行

部署与运行实战项目,需要先启动RocketMQ的Name Server和Broker,然后启动订单服务和支付服务。订单服务和支付服务都需要配置消息生产者和消费者,确保消息能够正常发送和接收。

// 启动RocketMQ的Name Server和Broker
nohup sh bin/mqnamesrv &
nohup sh bin/mqbroker -n 127.0.0.1:9876 &
// 订单服务
public class OrderService {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("OrderServiceProducer");
        producer.setNamesrvAddr("localhost:9876");
        producer.start();
        Message msg = new Message("OrderCreateTopic", // Topic
                "TagA", // Tag
                ("Order Id: 1").getBytes(RemotingHelper.DEFAULT_CHARSET), // body
                1000); // timeout
        producer.send(msg);
        producer.shutdown();
    }
}

// 支付服务
public class PaymentService {
    public static void main(String[] args) throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("PaymentServiceConsumer");
        consumer.setNamesrvAddr("localhost:9876");
        consumer.subscribe("OrderCreateTopic", "*");
        consumer.registerMessageListener((MessageListenerOrderly) (msgs, context) -> {
            for (MessageExt msg : msgs) {
                String orderId = new String(msg.getBody());
                // 处理订单创建消息
                onOrderCreated(orderId);
            }
            return ConsumeOrderlyStatus.SUCCESS;
        });
        consumer.start();
    }
}

部署并运行上述代码后,订单服务会发送订单创建消息到RocketMQ,支付服务会接收到订单创建消息后发送支付通知消息,订单服务会接收到支付成功的消息后更新订单状态。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消