一、入门
2.1.导入依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.8.0</version>
</dependency>
2.2.生产者
步骤分析
-
创建producer组
-
设置NameServer地址
-
startr生产者
-
发送消息获取结果
-
结束producer
public class Producer {
public static void main(String[] args) throws Exception {
//1.创建生产者组
DefaultMQProducer producer = new DefaultMQProducer("producer-hello");
//2.设置NameServer地址
producer.setNamesrvAddr("127.0.0.1:9876");
//3.启动producer实例
producer.start();
//4.创建消息
Message message = new Message("log-topic", "info-tag", "这是一个info信息".getBytes(RemotingHelper.DEFAULT_CHARSET));
//5.发送消息
SendResult result = producer.send(message);
//6.关闭producer实例
System.out.println("发送完毕,结果: "+result);
}
}
2.3.消费者
-
创建consumer组
-
设置Name Server地址
-
设置消费位置,从最开始销毁
-
设置消息回调处理监听 -> 处理消息
-
Start consumer
public class Consumer {
public static void main(String[] args) throws MQClientException {
//1.创建消费者组
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-hello");
//2.设置NameServer地址
consumer.setNamesrvAddr("127.0.0.1:9876");
//3.订阅topic,指定tag标签
consumer.subscribe("log-topic","info-tag");
//4.注册消息监听器
consumer.registerMessageListener(new MessageListenerConcurrently(){
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
System.out.printf("%s 接收到新的消息: %n", Thread.currentThread().getName());
msgs.stream().forEach(messageExt -> {
String body = null;
try {
body = new String(messageExt.getBody(), RemotingHelper.DEFAULT_CHARSET);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println(body);
});
//失败消费,稍后尝试消费,会进行多次重试
//return ConsumeConcurrentlyStatus.RECONSUME_LATER;
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5.启动消费者
consumer.start();
System.out.println("消费者启动...");
}
}
三、RocketMQ 核心概念
3.1核心原理
1.RocketMQ 网络部署特点
为了增强Broker性能与吞吐量,Broker一般都是以集群形式出现的。各集群节点中可能存放着相同Topic的不同Queue。
不过,这里有个问题,如果某Broker节点宕机,如何保证数据不丢失呢?其解决方案是,将每个Broker集群节点进行横向扩展,即将Broker节点再建为一个HA集群,解决单点问题。
Broker节点集群是一个主从集群,即集群中具有Master与Slave两种角色。Master负责处理读写操作请求,Slave负责对Master中的数据进行备份。当Master挂掉了,Slave则会自动切换为Master去工作。所以这个Broker集群是主备集群。Consumer既可以从Master订阅消息,也可以从Slave订阅消息
一个Master可以包含多个Slave,但一个Slave只能隶属于一个Master。 Master与Slave 的对应关系是通过指定相同的BrokerName、不同的BrokerId 来确定的。BrokerId为0表示Master非0表示Slave。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。
2.RabbitMQ工作流程(重要)
-
启动NameServer,NameServer起来后监听端口,等待Broker、Producer、Consumer连上来,相当于一个路由控制中心。
-
Broker启动,跟所有的NameServer保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有Topic信息。注册成功后,NameServer集群中就有Topic跟Broker的映射关系。
-
收发消息前,先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic。
-
Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取当前发送的Topic存在哪些Broker上,轮询从队列列表中选择一个队列,然后与队列所在的Broker建立长连接从而向Broker发消息。
-
Consumer跟Producer类似,跟其中一台NameServer建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,开始消费消息
3.2.Producer 生产者
RocketMQ提供多种发送方式,同步发送、异步发送、顺序发送、单向发送
。同步和异步方式均需要Broker返回确认信息,单向发送不需要。
RocketMQ中的消息生产者都是以生产者组( Group)的形式出现的。生产者组是同一类生产者的集合,这类Producer发送相同Topic类型的消息。一个生产者组可以同时发送多个主题的消息。
Producer会使用一定的算法选择把消息发送到哪个master的某个queue中。
3.3.Consumer 消费者
Consumer 支持两种消费形式:拉取式消费、推动式消费
。(主动,被动),RocketMQ中的消息消费者都是以消费者组(Consumer Group)的形式出现的。消费者组是同一类消费者的集合,这类Consumer消费的是同一个Topic类型的消息,不同的 Consumer Group可以消费同一个Topic。
一个Consumer Group内的Consumer可以消费多个Topic的消息。
[注意] 一个Queue是不能被同一个ConsumerGroup中的多个Consumer消费的
,目的是减少资源竞争提升整体性能。
3.4.Topic 消息主题
Topic表示一类消息的集合,每个topic主题包含若干条message消息,
每条message消息只能属于一个topic主题,Topic是RocketMQ进行消息订阅的基本单位。
3.5.Message
消息是指,消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题。
3.6.Tag 标签
为消息设置的标志,用于同一主题下区分不同类型的消息
。来自同一业务单元的消息,可以根据不同业务目的在同一主题下设置不同标签。标签能够有效地保持代码的清晰度和连贯性,并优化RocketMQ提供的查询系统。消费者可以根据Tag实现对不同子主题的不同消费逻辑,实现更好的扩展性。Topic是消息的一级分类,Tag是消息的二级分类
3.7.MessageQueue队列
一个Topic中可以包含多个Queue
,一 个Topic的Queue也被称为一个Topic中消息的分区(Partition)。 在一个Consumer Group内,一个Queue最多只能分配给一个Consumer
,一个Cosumer可以分配得到多个Queue。这样的分配规则,每个Queue只有一个消费者,可以避免消费过程中的多线程处理和资源锁定,有效提高各Consumer消费的并行度和处理效率。
消费者组中Consumer的数量应该小于等于订阅Topic的Queue数量。如果超出Queue数量,则多出的 Consumer将不能消费消息。如果一个Consmer挂了,该Consumer Group中的其它Consumer可以接着消费原Consumer消费的Queue。
【注意】 一个Topic可以对应多个消费者 ,一个Queue只能对应一个组中的一个消费者。
【注意】为了防止消息紊乱,一个Consumer Group 中的Consumer都是订阅相同Topic下的Queue。