【中间件】RocketMQ简单源码与基本使用

本文深入探讨RocketMQ的源码,涵盖生产者、消费者、消息重试、消息模式等核心概念。讲解了同步、异步、单向发送方式,并分析了消息消费失败的处理策略。此外,还介绍了顺序消息、广播消息、延迟消息和事务消息的实现原理及应用场景,强调了消费模式的拉和推。最后,提到了RocketMQ的ACL权限控制、消息轨迹和日志系统。

rocketMQ开发语言为java,代码阅读比较友好(让我努力一把捡起java)

注意所有的示例都要先启动消费者,再启动生产者,否则没监听的消息都丢了...

源码版\docs\cn\下有很多详细中文文档,\example\src\main\java\org\apache\rocketmq\example\下有很多样例,可以配合查看。

quickstart

部署的时候为了测试,我们调用了quickstart生产和消费了1000条消息,打开example下的quickstart文件夹,生产者:Producer.java

/*
 * Instantiate with a producer group name.
 */
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");

/*
 * Specify name server addresses.
 * <p/>
 *如果配置了系统环境变量,代码中不需要指定,如果没有,代码中需要指定。代码中优先级高于系统环境变量的配置
 * Alternatively, you may specify name server addresses via exporting environmental variable: NAMESRV_ADDR
 * <pre>
 * {@code
 * producer.setNamesrvAddr("name-server1-ip:9876;name-server2-ip:9876");
 * }
 * </pre>
 */
//启动
producer.start();

for (int i = 0; i < 1000; i++) {
    try {

        /*
         * Create a message instance, specifying topic, tag and message body.
         */
        Message msg = new Message("TopicTest" /* Topic */,
            "TagA" /* Tag 与kafka、rocketMQ不同,有特殊属性tag:可以对消息快速过滤*/,
            ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body 消息体:会转成一个二进制数组*/
        );

        /*
         * Call send message to deliver message to one of brokers.
         */
        SendResult sendResult = producer.send(msg);

        System.out.printf("%s%n", sendResult);
    } catch (Exception e) {
        e.printStackTrace();
        Thread.sleep(1000);
    }
}

/*关闭
 * Shut down once the producer instance is not longer in use.
 */
producer.shutdown();

再看消费者,同文件夹下的Consumer.java

//先new消费者
/*各种属性设置
 * Specify name server addresses.
 * <p/>
 *
 * Alternatively, you may specify name server addresses via exporting environmental variable: NAMESRV_ADDR
 * <pre>
 * {@code
 * consumer.setNamesrvAddr("name-server1-ip:9876;name-server2-ip:9876");
 * }
 * </pre>
 */

//*表示接收所有Tag,也可以指定tag
consumer.subscribe("TopicTest", "*");

/*  回调函数打印出消费完成
 *  Register callback to execute on arrival of messages fetched from brokers.
 */
consumer.registerMessageListener(new MessageListenerConcurrently() {

    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
        ConsumeConcurrentlyContext context) {
        System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});

simple

生产

rocketmq生产者消息发送有三种方式:同步、异步、单向

官方认为同步和异步都是可靠消息,因为都会收到回复,只是同步消息当时就会收到回复,异步消息是稍后获得回复。

消费失败

消息本身的原因:例如反序列化失败,消息数据本身无法处理(例如话费充值,当前消息的手机号被注销,无法充值)等。

        这种错误通常需要跳过这条消息,再消费其它消息,而这条失败的消息即使立刻重试消费,99%也不成功,所以最好提供一种定时重试机制,即过10秒后再重试或放入重试队列去延迟消费。

依赖的下游应用服务不可用:例如db连接不可用,外系统网络不可达等

        遇到这种错误,即使跳过当前失败的消息,消费其他消息同样也会报错。这种情况建议应用sleep 30s,再消费下一条消息,这样可以减轻Broker重试消息的压力。

注意到了吗?不管是自身原因还是下游原因,消费失败后是不能立刻重试的,因为几乎可以肯定会依然失败,如果不加以控制就会造成消息积压。

消息重试与消息重投

生产者在发送消息时,同步消息失败会重投,异步消息有重试,单向消息没有任何保证。

异步消费消息失败后可以重试,令消息再消费1-n次,可以设置重试次数和,重试不切换broker,因为broker也可能down,所以不保证消息不丢。

同步消息失败后会重投(换到别的broker),保证消息尽可能发送成功、不丢失,但可能会造成消息重复。

消息重复在RocketMQ中是无法避免的问题。消息重复在一般情况下不会发生,当出现消息量大、网络抖动,消息重复就会是大概率事件。另外,生产者主动重发、consumer负载变化也会导致重复消息。应该在消费端做幂等性保障以消除重复消息的影响。

ps:redis做消息队列有个很大的优势就是可以很方便的合并大量重复消息,如果你的系统会产生大量重复消息,不妨考虑。

同步发送

常用于重要的消息通知、短信通知

打开simple\Producer.java

//同步发送
public class Producer {
    public static void main(String[] args) throws MQClientException, InterruptedException {

        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        producer.start();

        for (int i = 0; i < 128; i++)
            try {
                {
                //消息体
                Message msg = new Message("TopicTest",
                    "TagA",
                    "OrderID188",
                    "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
                //发送消息,等待producer返回发送结果
                SendResult sendResult = producer.send(msg);
                //打印发送结果
                System.out.printf("%s%n", sendResult);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        producer.shutdown();
    }
}

异步发送

通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应

example\simple\AsyncProducer.java

public class AsyncProducer {
    public static void main(
        String[] args) throws MQClientException, InterruptedException, UnsupportedEncodingException {

        DefaultMQProducer producer = new DefaultMQProducer("Jodie_Daily_test");
        producer.start();
        producer.setRetryTimesWhenSendAsyncFailed(0);

        int messageCount = 100;
        //并发编程
        final CountDownLatch countDownLatch = new CountDownLatch(messageCount);
        for (int i = 0; i < messageCount; i++) {
            try {
                final int index = i;
                Message msg = new Message("Jodie_topic_1023",
                    "TagA",
                    "OrderID188",
                    "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
                //producer.send发送了msg和一个callback方法,但是producer.send本身没有返回对象,这样,如果producer有响应会直接调用callback方法来SendResult,producer.send本身不需要等待
                producer.send(msg, new SendCallback() {
                    @Override
                    public void onSuccess(SendResult sendResult) {
                        //一个很巧妙的计数器,只有收到返回消息才会让countDown,即-1,结尾时减完才继续执行shutdown,避免还有未返回时就shutdown接下来的返回无法接收
                        countDownLatch.countDown();
                        System.out.printf("%-10d OK %s %n", index, sendResult.getMsgId());
                    }

                    @Override
                    public void onException(Throwable e) {
                        countDownLatch.countDown();
                        System.out.printf("%-10d Exception %s %n", index, e);
                        e.printStackTrace();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        countDownLatch.await(5, TimeUnit.SECONDS);
        producer.shutdown();
    }
}

单向发送

常用于日志发送等不太在意发送结果的场景

public class Pr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值