【rocketMq-1】基础入门

本文深入讲解RocketMQ的部署架构及特性,包括角色介绍、消息发送与接收方式、集群部署细节等,并提供了Java示例代码及Spring整合教程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:RocketMQ 部署架构

1、RocketMQ的角色介绍

Producer:消息的发送者
ProducerGroup:同一类producer的集合
Consumer:消息接收者;
PushConsumer : broker主动推送消息给消费端
PullConsumer :消费端主动拉取消息
ConsumerGroup:同一类consumer的集合
Broker:暂存和传输消息;  一个broker中可以存储多个topic(一个topic也可以分片在多个broker上)
NameServer:管理Broker
Topic:区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以订阅一个或者多个Topic消息
Message Queue:相当于是Topic的分区,一个topic下可以挂载多个queue,消息真实的物理存储结构;
广播消息: 所有消费者都会消费该消息,哪怕是同一个ConsumerGroup的消费者
集群消费:一条消息只会被同一个ConsumerGroup下的一个消费者消费,如果是多条消息,会均衡消费
集群部署如上图:
NameServer:无状态节点,可集群部署,节点间无信息同步。
Broker

mater:slave一对多,通过指定相同的BrokerName建立关系。

BrokerId为0为master,非0为slave,slave中只有为1的那个可参与读负载。

每个broker与nameServer中所有节点建立长连接,定时注册topic信息上去。

Producer:  与nameServer中随机一个节点建立长连接,定期获取topic路由信息,并与提供topic服务的master建立长连接,定时发送心跳。   无状态,可集群部署。
consumer

同producer,另外也会与slave建立长连接和发送心跳。

可以从master或slave订阅消息,如果是master,则master会根据最大偏移量和拉取偏移量对比,(判断是否读取老消息,老消息已持久化,会产生IO),以及slave是否可读等,建议下一次从master还是slave拉取。

了解了物理集群后,来看看逻辑上的关系

如上图示:

  1. 在broker集群中,每个broker是可以存储多个topic的,当然,每个topic也是可以分散到不同的broker上。
  2. 在一个broker的每个topic分片下,有多个messageQueue(数量是创建topic时候指定的)。
  3. 那么每一个consumerGroup中的所有consumer,会轮询消费这些messageQueue(集群消费模式如图2)。   所以,consumer的数量不能超过messageQueue的数量
执行流程:
  1. 启动NameServer,监听端口,等待broker、producer、consumer的连接, 相当于一个路由控制中心。
  2. broker启动与NameServer建立连接,定时发送心跳包(包含broker信息:ip、port、存储的所有topic信息)。
  3. 手动创建topic,指明要存储在那些broker上(也可以发送消息时候创建)
  4. producer发消息,启动时先与一台NameServer建立长连接,并获取到要发送的topic在哪些broker,轮询选择一个队列,与所在的broker建立长连接并发送消息。
  5. consumer启动与producer类似。
2、rocketMq特性
了解了基本的角色后,来看看相关特性

Tag:统一topic下区分不同类型消息的

顺序消息: 消费一类消息时,可以保证按发送的顺序依次消费,比如一个订单的创建,支付,完成三条消息。   不同的订单之间可并行

普通顺序消息 : 顺序消息的一种,正常情况保证顺序,如果broker宕机队列数发生变化,哈希取模定位队列也会发生变化,造成错乱。

严格顺序消息:一个broker挂机,全都不能使用,可以保证顺序性。  但是可用性差

消息过滤: consumer根据tag标签来消费自己想要的消息,类似rabbitmq中路由键一样。

回溯消费:   broker提供的机制,consumer可重新消费一定时间内,已经消费过的消息

事务消息: 可将本地事务和发送消息的操作定义到全局事务中,保证同时成功或失败

定时消息: rocket中自带了一个topic:SCHEDULE_TOPIC_XXXX,该topic下绑定了代表不同定时时间的queue。 如果发送定时消息,会先发到该topic,并根据具体时间路由都对应的queue中,并在达到合适的时间后发送到业务topic中。

默认时间有18个等级,delayTimeLevel:1-18(18个queue,queueId=level-1): 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h。

  • level == 0,消息为非延迟消息
  • 1<=level<=maxLevel,消息延迟特定时间,例如level==1,延迟1s
  • level > maxLevel,则level== maxLevel,例如level==20,延迟2h

消息重试:

消费失败通常有两种原因,1:消息本身反序列化失败,即使立即重试也不会成功。所以最好提供一种定时重试机制,不要频繁重试。    2、下游程序原因,比如db连接不可用,可以让应用sleep一段时间,再消费下一条消息,减轻broker重试消息的压力。

消息重投

retryTimesWhenSendFailed

同步消息重投,设置重投次数,默认2。

重投不会选择上一次失败的broker,最大保证成功。   

如果超过重试次数,抛出异常由客户端自己保证消息不丢失。

RemotingException、 MQClientException和部分MQBrokerException时会重投

retryTimesWhenSendAsyncFailed:异步发送失败重试次数,异步重试不会选择其他broker, 仅在同一个broker上做重试,不保证消息不丢。

retryAnotherBrokerWhenNotStoreOK:消息刷盘(主或备)超时或slave不可用(返回状态非SEND_OK),是否尝试发送到其他broker,默认false。十分重要消息可以开启。

流量控制:

1) 生产者流控:因broker处理能力达到瓶颈

2) 消费者流控,因为消费能力达到瓶颈

死信队列: 消费失败且超过重试次数的消息,会发送到死信队列

3、java中的使用
了解了基本概念后,看一下基本api的使用

 1)、同步发送消息

import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;

public class MyProducer {
    public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException, RemotingException, MQClientException, MQBrokerException {
        // 在实例化生产者的同时,指定了生产组名称 
        DefaultMQProducer producer = new DefaultMQProducer("myproducer_grp_01");
        // 指定NameServer的地址 
        producer.setNamesrvAddr("node1:9876");
        // 对生产者进行初始化,然后就可以使用了 
        producer.start();
        // 创建消息,第一个参数是主题名称,第二个参数是消息内容 
        Message message = new Message("tp_demo_01", "hello lagou 01".getBytes(RemotingHelper.DEFAULT_CHARSET));
        // 发送消息 
        final SendResult result = producer.send(message);
        System.out.println(result);
        // 关闭生产者 producer.shutdown();
    }
}

2)、异步发送消息

import org.apache.rocketmq.client.exception.MQClientException;
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;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;

public class MyAsyncProducer {
    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException {
        // 实例化生产者,并指定生产组名称
        DefaultMQProducer producer = new DefaultMQProducer("producer_grp_01");
        // 指定nameserver的地址
        producer.setNamesrvAddr("node1:9876");
        // 初始化生产者
        producer.start();
        for (int i = 0; i < 100; i++) {
            Message message = new Message("tp_demo_02", ("hello lagou " + i).getBytes("utf-8"));
            // 消息的异步发送
            producer.send(message, new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    System.out.println("发送成功:" + sendResult);
                }

                @Override
                public void onException(Throwable throwable) {
                    System.out.println("发送失败:" + throwable.getMessage());
                }
            });
        }
        // 由于是异步发送消息,上面循环结束之后,消息可能还没收到broker的响应 
        // 如果不sleep一会儿,就报错 
        Thread.sleep(10_000);
        // 关闭生产者 
        producer.shutdown();
    }
}

3)、消费者拉取消息

import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Set;

/**
 * 拉取消息的消费者
 */
public class MyPullConsumer {
    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException, UnsupportedEncodingException {
        // 拉取消息的消费者实例化,同时指定消费组名称
        DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("consumer_grp_01");
        // 设置nameserver的地址
        consumer.setNamesrvAddr("node1:9876");
        // 对消费者进行初始化,然后就可以使用了
        consumer.start();
        // 获取指定主题的消息队列集合
        final Set<MessageQueue> messageQueues = consumer.fetchSubscribeMessageQueues("tp_demo_01");
        // 遍历该主题的各个消息队列,进行消费
        for (MessageQueue messageQueue : messageQueues) {
            // 第一个参数是MessageQueue对象,代表了当前主题的一个消息队列
            // 第二个参数是一个表达式,对接收的消息按照tag进行过滤
            // 支持"tag1 || tag2 || tag3"或者 "*"类型的写法;null或者"*"表示不对 消息进行tag过滤
            // 第三个参数是消息的偏移量,从这里开始消费
            // 第四个参数表示每次最多拉取多少条消息
            final PullResult result = consumer.pull(messageQueue, "*", 0, 10);
            // 打印消息队列的信息
            System.out.println("message******queue******" + messageQueue);
            // 获取从指定消息队列中拉取到的消息
            final List<MessageExt> msgFoundList = result.getMsgFoundList();
            if (msgFoundList == null) continue;
            for (MessageExt messageExt : msgFoundList) {
                System.out.println(messageExt);
                System.out.println(new String(messageExt.getBody(), "utf- 8"));
            }
        }
        // 关闭消费者
        consumer.shutdown();
    }
}

4)、主动推送消息给消费者

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.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;

import java.io.UnsupportedEncodingException;
import java.util.List;

/**
 * 推送消息的消费者
 */
public class MyPushConsumer {
    public static void main(String[] args) throws MQClientException, InterruptedException {
        // 实例化推送消息消费者的对象,同时指定消费组名称
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_grp_02");
        // 指定nameserver的地址
         consumer.setNamesrvAddr("node1:9876");
        // 订阅主题
        consumer.subscribe("tp_demo_02", "*");
        // 添加消息监听器,一旦有消息推送过来,就进行消费
        consumer.setMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                final MessageQueue messageQueue = context.getMessageQueue();
                System.out.println(messageQueue);
                for (MessageExt msg : msgs) {
                    try {
                        System.out.println(new String(msg.getBody(), "utf- 8"));
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }
                // 消息消费成功
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                // 消息消费失败
                // return ConsumeConcurrentlyStatus.RECONSUME_LATER;
            }
        });
        // 初始化消费者,之后开始消费消息
        consumer.start();
        // 此处只是示例,生产中除非运维关掉,否则不应停掉,长服务
        // Thread.sleep(30_000); // 关闭消费者
        // consumer.shutdown();
    }
}
4、spring整合

使用原生api后,再来看看与spring整合

生产者:

1)添加依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
</parent>
<properties>
    <rocketmq-spring-boot-starter-version>2.0.3</rocketmq-spring-boot- starter-version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
        <version>${rocketmq-spring-boot-starter-version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.6</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2)配置文件

# application.properties
rocketmq.name-server=192.168.80.121:9876;192.168.80.122:9876
rocketmq.producer.group=my-group

3) 测试类

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MQSpringBootApplication.class})
public class ProducerTest {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    @Test
    public void test1(){
        rocketMQTemplate.convertAndSend("springboot-mq","hello springbootrocketmq");
    }
}
消息消费者

1) 依赖和配置文件同生产者

2) 消息监听器

@Slf4j
@Component
@RocketMQMessageListener(topic = "springboot-mq",consumerGroup = "springboot-mq-consumer-1") 
public class Consumer implements RocketMQListener<String> { 
    @Override 
    public void onMessage(String message) { 
        log.info("Receive message:"+message); 
    } 
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值