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

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

3849

被折叠的 条评论
为什么被折叠?



