消息队列——RocketMQ示例
其他
2020-01-11 00:13:00
阅读次数: 0
消息队列——RocketMQ示例
1. 简介
- 定位:分布式消息中间件、消息队列
- 语言:Java
- 性能:10万级吞吐量,ms级时效性
- 可靠性:分布式架构,可靠性非常高
- 其他:由阿里在2016年贡献至Apache基金会,已成为顶级项目。历经双十一考验,能够处理万亿级别的消息。
2. 集群架构与工作流程
- 集群架构示意图(来自互联网)
- 工作流程
- 首先,启动NameServer集群,负责管理Broker、Producer、Consumer的连接、负责管理Topic的元信息。(作用类似于Kafka中的ZooKeeper)
- 接着,启动Broker集群,注册到NameServer集群中,保持心跳。一个集群可以由多个Broker组组成,一个Broker组(由BrokerName标识)包括Master、Slave(由BrokerId标识),Master负责接收生产的数据,Slave负责备份Master的数据。(同Kafka差异较大)
- 同步:数据生产到Master,并将数据同步到Slave后,才向生产端回应Ack
- 异步:数据生产到Master后,马上向生产端回应Ack,不管Slave数据是否已经同步
- 创建一个Topic,Topic可以手动指定将数据存储到哪些Broker,也可以自动分配。(没有Topic的话,发送时也能自动创建)
- Producer生产数据时,会与NameServer集群保持长连接,定期获取对应Topic的信息,找到对应Master节点并发送数据。注意,Producer只能向Broker中的Master生产数据。
- Consumer消费数据时,会与NameServer集群保持长连接,定期获取对应Topic的信息,找到对应Master、Slave节点并获取数据(包括Push、Pull)。注意,Consumer可以从Master或Slave消费数据(由Broker配置决定)。
- Push方式: 由Broker推消息到Consumer
- Pull方式: 由Consumer主动从Broker拉数据
- 消息顺序:一个Broker内有多个MessageQueue,消息在一个MessagQueue内是有序的。(和Kafka类同,Kafka的消息在一个Partition内有序)
3. 简单示例
- Maven依赖导包
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.6.0</version>
</dependency>
- 生产者 (同步、异步、单向)
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
public class SimpleProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("producer_name");
producer.setNamesrvAddr("192.168.1.110:9876;192.168.1.111:9876;192.168.1.112:9876");
producer.start();
for (int i = 0; i < 100; i++) {
Message msg = new Message(
"Topic_Test",
"Tag_A",
("Hello RocketMQ " + i).getBytes()
);
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
}
producer.shutdown();
}
}
- 消费者 (Push方式、Pull方式)
import org.apache.rocketmq.client.consumer.DefaultLitePullConsumer;
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.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import java.util.List;
public class SimpleConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer pushConsumer = new DefaultMQPushConsumer("push_consumer_name");
pushConsumer.setNamesrvAddr("192.168.1.110:9876;192.168.1.111:9876;192.168.1.112:9876");
pushConsumer.subscribe("Topic_Test", "Tag_A");
pushConsumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt messageExt : list) {
byte[] body = messageExt.getBody();
System.out.println(new String(body));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
pushConsumer.start();
}
}
4. 有序消息示例
- 原理:将需要保证顺序的消息发送到同一MessageQueue,即可保证有序
- 生产者
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import java.util.List;
public class OrderedProducer {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("producer_name");
producer.setNamesrvAddr("192.168.1.110:9876;192.168.1.111:9876;192.168.1.112:9876");
producer.start();
for (int i = 0; i < 100; i++) {
int orderId = i % 10;
Message msg = new Message(
"Topic_Ordered",
"Tag_A",
"KEY" + i,
("Hello RocketMQ " + i).getBytes()
);
SendResult sendResult = producer.send(
msg,
new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
int idx = (Integer) arg % mqs.size();
return mqs.get(idx);
}
},
orderId
);
System.out.printf("%s%n", sendResult);
}
producer.shutdown();
}
}
- 消费者
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.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
public class OrderedConsumer {
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group_name");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("Topic_Ordered", "Tag_A");
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
for (MessageExt messageExt : msgs) {
System.out.println(
Thread.currentThread().getName() +
" Receive New Messages: " + new String(messageExt.getBody())
);
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
}
}
5. 事务消息示例
- 事务机制示意图(来自互联网)
- 生产者生产消息到RocketMQ集群
- RocketMQ集群返回Ack,表示生产成功
- 生产者处理本地事务
- 事务的状态
- 如果成功,提交 LocalTransactionState.COMMIT_MESSAGE
- 如果失败,回滚 LocalTransactionState.ROLLBACK_MESSAGE
- 仍在处理中, 未知 LocalTransactionState.UNKNOW
- RocketMQ集群在未收到COMMIT_MESSAGE或ROLLBACK_MESSAGE前,会定时回查生产者
- 确认消息事务状态(提交、回滚、未知),直到状态变为提交或回滚
- 生产者
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.message.Message;
public class TransactionProducer {
public static void main(String[] args) throws Exception {
TransactionMQProducer producer = new TransactionMQProducer("transaction_producer_name");
producer.setNamesrvAddr("192.168.1.110:9876;192.168.1.111:9876;192.168.1.112:9876");
producer.setTransactionListener(new MyTransactionListener());
producer.start();
for (int i = 0; i < 100; i++) {
String suffix = i % 5 == 0 ? "!@#" : i + "";
String body = "Hello RocketMQ ," + suffix;
Message msg = new Message(
"Topic_Transaction",
"Tag_A",
body.getBytes()
);
SendResult sendResult = producer.sendMessageInTransaction(msg, null);
System.out.printf("%s%n", sendResult);
}
producer.shutdown();
}
}
- 事务处理监听器
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.concurrent.ConcurrentHashMap;
public class MyTransactionListener implements TransactionListener {
private ConcurrentHashMap<String, Integer> transactionMap = new ConcurrentHashMap<>();
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
transactionMap.put(msg.getTransactionId(), 3);
new Thread(() -> doSomething(msg, arg)).start();
return LocalTransactionState.UNKNOW;
}
private void doSomething(Message msg, Object arg) {
String content = new String(msg.getBody());
String[] fields = content.split(",");
try {
String txt = fields[0];
int number = Integer.parseInt(fields[1]);
System.out.println("txt = " + txt + ", number = " + number);
transactionMap.put(msg.getTransactionId(), 1);
} catch (NumberFormatException e) {
e.printStackTrace();
transactionMap.put(msg.getTransactionId(), 0);
}
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
Integer integer = transactionMap.get(msg.getTransactionId());
if (integer != null) {
switch (integer) {
case 0:
return LocalTransactionState.COMMIT_MESSAGE;
case 1:
return LocalTransactionState.ROLLBACK_MESSAGE;
default:
return LocalTransactionState.UNKNOW;
}
}
return LocalTransactionState.UNKNOW;
}
}
发布了128 篇原创文章 ·
获赞 45 ·
访问量 15万+
转载自blog.csdn.net/alionsss/article/details/103860531