RocketMQ整理
文章目录
一、RocketMQ的相关理论
1.基本概念
1)消息生产者(Producer)
消息一般由业务系统产生,生产者将业务中产生的消息发送给Broker服务器。有多种发送方式:同步发送、异步发送、顺序发送、单向发送,使用同步或异步发送需要Broker返回确认信息。Producer与Name Server的一个随机节点建立长连接,定期从中获取Topic路由信息,并向提供Topic信息的Master建立长连接,且定时向Master发送心跳。
2)消息消费者(Consumer)
消息消费者从Broker服务器拉取消息,并将其提供给程序。从用户应用角度可分为两种消费方式:拉取式消费、推动式消费。Consumer与Name Server中的一个随机节点建立长连接,定期从中获取Topic路由信息,并向提供Topic路由信息的Master和Slave建立长连接,且定时向Master和Slave发送心跳。
3)代理服务器(Broker Server)
消息的中转角色,用来存储和转发消息。负责接收并存储Producer发送的消息,对Consumer发送的消息请求进行消息转发。Broker分为Master和Slave,一个Master可以有多个Slave,一个Slave只能有一个Master,通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0时表示Master,非0时表示Slave。每个Broker与Name Server中的所有节点建立长连接。(注:只有BrokerId=1的Slave才会参与消息的读负载)
4)主题(Topic)
表示一类消息的集合,每条消息只能属于一个Topic,一个Topic可以有多条消息,是RocketMQ进行消息订阅的基本单位。
5)名称服务(Name Server)
名称服务充当路由消息的提供者,Producer和Consumer通过名称服务查找各Topic所对应的Broker服务器。多个实例可组成集群,相互之间独立。
6)标签(Tag)
为消息设置的标识,用来区分同一Topic下不同类型的消息
7)消息队列(Message Queue)
Topic由一个或多个队列组成,Producer的发送机制使得消息尽量平均分布在多个队列中。RocketMQ保证的消息有序即分区(queue)消息的有序。
2.工作流程
- 启动Name Server,启动后监听端口,等待Broker、Producer、Consumer的连接,相当于路由控制中心;
- 启动Broker,跟所有的Name Server节点建立长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP + 端口)以及存储所有Topic信息。注册成功后,Name Server就有Topic跟Broker的映射信息;
- 收发消息前,先创建Topic,创建时需要指定该Topic存储在那些Broker上,也可以在发送消息时自动创建Topic;
- Producer发送消息,启动时先和Name Server中的一个节点建立长连接,并从Name Server中获取当前发送的Topic所对应的Broker,轮询从队列列表中选择一个队列,然后与队列所在的Broker建立长连接并发送消息。
- Consumer消费消息,启动时先和Name Server中的一个节点建立长连接,并从Name Server中获取当前订阅的Topic所对应的Broker,然后跟Broker建立连接通道,并进行消费。
二、RocketMQ的基础使用
导入依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.3.0</version>
</dependency>
1.基本样例
1)发送同步消息
必须等待消息持久化到磁盘中后,RocketMQ给生产者返回信息,才能继续执行后面的业务;
public static void main(String[] args) throws Exception {
//实例化消息生产者
DefaultMQProducer producer = new DefaultMQProducer("producerA");
//设置Name Server地址
producer.setNamesrvAddr("127.0.0.1:9876");
//启动生产者
producer.start();
//创建消息
Message messge = new Message(
"TopicTest", //指定Topic
"TagA", //指定Tag
"RocketMQ同步消息".getBytes("utf-8") //消息体
);
//发送消息
SendResult result = producer.send(message);
//通过SendResult返回消息判断是否成功送达
System.out.println("%s%n", result);
//不再发送消息,关闭连接
producer.shutdown();
}
2)发送异步消息
不需要等待消息持久化到磁盘中,就可以执行后面的业务,通常使用在对相应时间敏感的业务场景,即发送端不能长时间等待Broker响应;
public static void main(String[] args) throws Exception {
//实例化消息生产者
DefaultMQProducer producer = new DefaultMQProducer("producerB");
//设置Name Server地址
producer.setNamesrvAddr("127.0.0.1:9876");
//启动生产者
producer.start();
//创建消息
Message messge = new Message(
"TopicTest", //指定Topic
"TagB", //指定Tag
"RocketMQ异步消息".getBytes("utf-8") //消息体
);
//发送消息,SendCallback接收异步返回结果
producer.send(message, new SendCallback() {
//发送成功
@Overrride
public void onSuccess(SendResult result) {
System.out.println("消息发送成功");
}
//发送失败
@Override
public void onException(Throwable e) {
System.out.println("消息发送失败");
e.printStackTrace();
}
});
//等待信息返回,不能关闭连接
//producer.shutdown();
}
3)单向发送消息
用于不关心发送结果的业务场景,例如日志发送;
public static void main(String[] args) throws Exception {
//实例化消息生产者
DefaultMQProducer producer = new DefaultMQProducer("producerC");
//设置Name Server地址
producer.setNamesrvAddr("127.0.0.1:9876");
//启动生产者
producer.start();
//创建消息
Message message = new Message(
"TopicTest", //指定Topic
"TagC", //指定Tag
"RocketMQ单向消息".getBytes("utf-8") //消息体
);
//单向发送消息,没有返回结果
producer.sendOneway(message);
//不再发送消息,关闭连接
producer.shutdown();
}
4)消费消息
public static void main(String[] args) throws Exception {
//实例化消息消费者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumerA");
//设置Name Server地址
consumer.setNamesrvAddr("127.0.0.1:9876");
//订阅Topic并过滤Tag
consumer.subscribe("TopicTest", "*");
//注册回调实现类来处理从Broker获取到的消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n", msgs);
// 标记该消息已经被成功消费
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费者
consumer.start();
}
2.顺序消息
顺序消费即FIFO,RocketMQ可以确保消息消费有序,分为分区有序和全局有序;
原理:默认情况下会采取Round Robin轮询方式把消息发送到不同的queue(分区队列)。消息消费时如果从一个queue中拉取,此时保证全局有序;如果从多个queue中拉取时,则只能保证分区有序,即相对于每一个queue,消息的消费都是有序的。
3.延时消息
只需要给要发送的消息设置延迟发送时间即可。
设置延时等级5,表示消息1分钟后发送;
message.setDelayTimeLevel(5);
延时等级如下:
private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
4.批量消息
批量发送消息可以显著提高发送小消息的性能,限制是这批消息需要有相同的Topic和waitStoreMsgOK,且不能是延时消息。此外,这批消息总大小不能超过4M。不确定是否超过4M时最好进行消息列表分割。
List<Message> messages = new ArrayList<>();
messages.add(new Message("TopicA", "TagA", "批量消息第一条".getBytes("utf-8")));
messages.add(new Message("TopicA", "TagA", "批量消息第二条".getBytes("utf-8")));
messages.add(new Message("TopicA", "TagA", "批量消息第三条".getBytes("utf-8")));
messages.add(new Message("TopicA", "TagA", "批量消息第四条".getBytes("utf-8")));
5.过滤消息
Tag标志用来进行过滤选择想要的信息;
接受Topic为TopicTest下标志为TagA或TagB或TagC的信息:
consumer.subscribe("TopicTest", "TagA || TagB || TagC");
也可以通过使用SQL语句来进行复杂的选择:
例如两条信息如下:
|---------|
| message |
|---------|
|a = 7 |
|b = 'abc'|
|---------|
|---------|
| message |
|---------|
|a = 3 |
|b = 'abc'|
|---------|
如下为判断选择第一条
consumer.subscribe("TopicTest", MessageSelector.bySql("a > 5 and b = 'abc'"))
三、Springboot的集成使用
1.导入依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>
2.配置文件
1)生产者配置
server:
port: 8888
rocketmq:
name-server: 127.0.0.1:9876
producer:
group: pro-group
2)消费者配置
server:
port: 7777
rocketmq:
name-server: 127.0.0.1:9876
3.代码示例
1)消息生产者
@RestController
public class Prod {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@RequestMapping("/sendOne")
public String sendMsg(String message) throws Exception() {
//发送同步消息
rocketMQTemplate.syncSend("TopicA", message);
//发送异步消息
rocketMQTemplate.asyncSend("TopicA", message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("消费成功");
}
@Override
public void onException(Throwable e) {
System.out.println("消费失败");
}
});
//发送单向消息
rocketMQTemplate.sendOneWay("TopicA", message);
//发送延时消息,”5000“是超时时间,单位毫秒, “2”是延时等级
rocketMQTemplate.syncSend("TopicA", MessageBuilder.withPayload(message).build(), 5000, 2);
//过滤消息,需要使用 : 进行分隔
rocketMQTemplate.syncSend("TopicA:" + "TagA", message);
return "SUCCESS";
}
}
2)消息消费者
@Component
@RocketMQMessageListener(
//指定要监听的topic
topic = "TopicA",
//指定消费者组
consumerGroup = "consumer-group",
//指定消费模式为广播模式,为MessageModel.CLUSTERING时为集群模式
messageModel = MessageModel.BROADCASTING,
//指定Tag,过滤消息
selectorType = SelectorType.TAG,
selectorExpression = "TagA"
)
public class Consu implements RocketMQListener<Message> {
@Override
public void onMessage(MessageExt messageExt) {
System.out.println("消费消息" + new String(messageExt.getBody()));
}
}