RocketMq从入门到精通

1.RocketMQ的使用场景

•应用解耦

系统的耦合性越高,容错性就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。

•流量削峰

应用系统如果遇到系统请求流量的瞬间猛增,有可能会将系统压垮。有了消息队列可以将大量请求缓存起来,分散到很长一段时间处理,这样可以大大提到系统的稳定性和用户体验。

举例:业务系统正常时段的QPS如果是1000,流量最高峰是10000,为了应对流量高峰配置高性能的服务器显然不划算,这时可以使用消息队列对峰值流量削峰

•数据分发

通过消息队列可以让数据在多个系统之间进行流通。数据的产生方不需要关心谁来使用数据,只需要将数据发送到消息队列,数据使用方直接在消息队列中直接获取数据即可

2.RocketMQ的角色介绍

  • Producer:消息的发送者;举例:发信者

  • Consumer:消息接收者;举例:收信者

  • Broker:暂存和传输消息;举例:邮局

  • NameServer:管理Broker;举例:各个邮局的管理机构

  • Topic:区分消息的种类,一个tocic对应多条消息;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以订阅一个或者多个Topic消息

  • Message Queue:相当于是Topic的分区;用于并行发送和接收消息

2.1什么是Broker?

在Apache RocketMQ中,Broker(代理服务器)是RocketMQ的核心组件之一,主要负责存储和传递消息。

Broker是消息队列服务的中间件节点,它接收来自生产者的消息,并将这些消息存储在磁盘上,然后传递给相应的消费者。Broker充当了消息的中转站,负责消息的可靠存储和传输。

一个RocketMQ系统通常包含多个Broker组成的集群。每个Broker独立运行,具有自己的配置信息和存储空间。每个Broker可以管理多个主题(Topic),并负责维护主题的消息队列(Message Queue)。

当生产者将消息发送到Broker时,Broker会将消息存储在磁盘上的存储文件中,这样即使在Broker故障或重启后,仍然可以保留消息的持久性。消费者可以从Broker订阅并消费这些消息。

除了存储和传递消息外,Broker还负责管理主题的元数据信息、处理消息的复制和同步、维护消息的索引等。Broker之间通过网络进行通信,以实现主题和消息的分发、复制和负载均衡。

总结来说,Broker是RocketMQ的核心组件,负责存储和传递消息。它是RocketMQ中实际处理消息的节点,用于保证消息的可靠性、持久性和高性能传输。

举例:

当使用RocketMQ搭建一个电商平台的消息系统时,可以使用多个Broker组成的集群来处理消息的存储和传递。

假设在RocketMQ集群中有三个Broker,分别命名为Broker-A、Broker-B和Broker-C。下面是一个示例的消息处理过程:

  1. 生产者将订单消息发送到Broker-A。消息被存储在Broker-A的磁盘上,并且Broker-A会给消息生成一个唯一的消息ID。

  2. 消费者A订阅了订单主题,并从Broker-A消费消息。消费者A通过与Broker-A建立连接,并从Broker-A请求获取未消费的订单消息。

  3. Broker-A将未消费的订单消息传递给消费者A。消费者A按照消息的顺序进行消费,处理其中一个订单。

  4. 同时,消费者B也订阅了订单主题,但连接到的是Broker-B。消费者B获取与自己有关的未消费的订单消息。

  5. Broker-A和Broker-B之间进行消息的同步和复制。当Broker-A接收到新的订单消息时,它会将消息复制到Broker-B,以确保消息的可靠性和高可用性。

  6. 消费者B从Broker-B获取未消费的订单消息,并按照顺序进行处理。

通过这样的方式,多个Broker组成的集群可以同时处理大量的消息,并提供高可用性和可靠性。当一个Broker故障或不可用时,其他Broker可以接管它的工作,确保消息系统的正常运行。

总结来说,RocketMQ的Broker组成的集群可以处理消息的存储和传递,实现高吞吐量、高可用性和可靠性的消息系统。不同的Broker之间通过复制和同步机制来保证消息的一致性和持久性,并由消费者从相应的Broker消费消息。

2.2什么是NameServer?

在Apache RocketMQ中,NameServer(命名服务器)是一个用于管理和维护Broker集群信息的关键组件。

NameServer充当了RocketMQ集群的注册中心,它负责维护所有Broker的网络地址、主题(Topic)的路由信息以及消费者的订阅关系。

当生产者发送消息到RocketMQ集群时,它首先需要与NameServer进行交互,以获取特定主题的消息队列在哪些Broker上,并且按照一定的负载均衡策略选择一个合适的Broker发送消息。

消费者在订阅主题之前,也需要与NameServer联系,以获取订阅主题的路由信息,包括哪个Broker上有该主题的消息队列以及消费者从哪些队列消费。

当集群中有新的Broker加入或已有的Broker下线时,NameServer负责更新Broker的注册信息,并通知生产者和消费者相应的变化。

NameServer本身是无状态的,可以水平扩展以应对高负载和高可用的需求。可以通过运行多个NameServer节点形成一个集群,并使用负载均衡或域名解析来进行NameServer节点的发现。

总结来说,NameServer是RocketMQ集群中的关键组件,用于管理和维护Broker集群信息、主题的路由信息和消费者的订阅关系。它提供了生产者和消费者与RocketMQ集群交互的入口点,为消息的发送和消费提供了必要的服务。

举例:

假设有一个名为"ecommerce"的RocketMQ集群,其中包含三个Broker(Broker-A、Broker-B、Broker-C)和一个NameServer节点。

  1. 启动RocketMQ集群:首先,启动三个Broker,它们会向NameServer注册并提供自己的网络地址和主题信息。

  2. 注册Broker信息:每个Broker在启动时会向NameServer节点注册自己的信息。例如,Broker-A向NameServer注册自己的地址(IP和端口)以及它所负责的主题和消息队列的信息。

  3. 生产者发送消息:一个电商应用的订单服务作为RocketMQ的生产者,要将订单消息发送到主题"order_topic"。生产者首先与NameServer进行通信,请求获取"order_topic"的路由信息,NameServer会返回包含该主题消息队列在哪些Broker上的信息。

  4. 负载均衡选择Broker:生产者使用一种负载均衡算法(如轮询或随机选择)从可用的Broker列表中选择一个Broker,然后将订单消息发送到该Broker。

  5. NameServer更新Broker注册信息:如果此时存在新的Broker加入集群(例如Broker-D),或已有的Broker下线(例如Broker-C),NameServer会接收并更新Broker的注册信息。

  6. 消费者订阅主题:一个订单处理服务作为RocketMQ的消费者,通过与NameServer联系,获取"order_topic"的路由信息,包括具体在哪个Broker上有消息队列以及消费者从哪些队列消费。

  7. 从Broker消费消息:消费者通过与相应的Broker(例如Broker-A)建立连接,并从它的消息队列中消费消息。消费者按照消息的顺序进行消费,处理订单消息。

总结来说,NameServer在RocketMQ集群中起到了注册中心的作用,维护Broker的网络地址、主题的路由信息以及消费者的订阅关系。通过与NameServer的交互,生产者和消费者可以获取到正确的Broker地址和消息队列信息,以实现消息的发送和消费。同时,NameServer还可以动态更新和调整Broker的注册信息,以适应集群的变化。

2.3什么是Message Queue?

Message Queue可以理解为一个逻辑上的消息分组,它将消息按照某种规则进行划分和管理。每个Message Queue都有一个唯一的标识符,通常由主题(Topic)和队列ID组成。在RocketMQ中,一个主题(Topic)可以被划分为多个消息队列(Message Queue),每个队列只会被一个消费者进行消费。这种划分可以提高消息的并行处理能力,允许多个消费者同时处理不同的消息队列,从而提高整体的消息处理吞吐量。使用消息队列,可以实现负载均衡和并行处理,同时还能保证消息的有序性。例如,当一个主题有大量的消息需要处理时,RocketMQ将根据一定的策略将这些消息均匀地分发到多个消息队列中,以使多个消费者可以并行地消费消息。通过使用消息队列,RocketMQ能够有效地处理大量的消息,并提供高吞吐量和可靠性。消息队列的使用对于构建可扩展和高性能的分布式消息系统是非常重要的。

举个例子:

当一个电商平台接收用户的订单时,可以使用RocketMQ来处理这些订单消息。假设电商平台的主题(Topic)是"orders",并配置了4个消息队列(Message Queue),编号分别为0、1、2、3。当用户下单时,订单消息将被发送到主题"orders"下的某个消息队列中。例如,第一个订单消息被发送到消息队列1(编号为1)中,第二个订单消息被发送到消息队列3(编号为3)中,以此类推。在消费端,有多个消费者(Consumer)订阅了主题"orders"。这些消费者可以并行地从不同的消息队列中消费订单消息。例如,消费者A从消息队列0(编号为0)中消费订单消息,消费者B从消息队列1(编号为1)中消费订单消息,消费者C从消息队列2(编号为2)中消费订单消息,消费者D从消息队列3(编号为3)中消费订单消息。通过这种方式,不同的消费者可以并行地消费订单消息,提高了整体的消息处理能力。另外,RocketMQ确保相同队列的消息按照发送顺序被消费,保证了订单的有序性处理。总结来说,RocketMQ中的消息队列(Message Queue)可以将订单消息划分为不同的逻辑分区,并允许多个消费者并行地消费这些消息,以提高系统的并发处理能力。

在RocketMQ中,同一个消息队列内的消息是有序的。这意味着,当消费者从同一个消息队列中消费消息时,消息的顺序将与它们被发送的顺序保持一致。对于订单处理来说,有序性是非常重要的。假设有一批订单消息按照发送顺序被放入同一个消息队列中,如果消费者可以按照任意顺序消费这些消息,那么就可能会导致订单处理的混乱,比如订单被错误地处理或处理的顺序不正确。RocketMQ通过将订单消息放入同一个消息队列,然后让消费者按照顺序从该队列中消费消息,确保了订单的有序性处理。也就是说,消费者A会按照消息的发送顺序依次处理订单消息,消费者B也会按照相同的顺序处理订单消息,以此类推。这样可以保证订单的处理在整个系统中是有序的。通过保证订单的有序性处理,电商平台可以更加可靠地处理订单,避免因为消息乱序而导致的错误处理。同时,RocketMQ还提供了消息重试的机制,以确保消息在异常情况下的可靠传输,进一步增强了订单处理的可靠性和一致性。

3.RocketMq的消息样例

3.1基本样例

我们使用消息生产者分别通过三种方式发送消息:同步发送,异步发送,单向发送

3.1.1单向发送

单向发送是一种异步的消息发送方式。单向发送意味着生产者在发送消息后不会等待或者接收任何来自RocketMQ的确认或响应。它一般用于一些无需关注消息是否发送成功的场景,比如日志记录、埋点统计等。在进行单向发送时,生产者只需调用发送消息的方法,将消息发送到指定的主题。例如,使用RocketMQ提供的Java客户端API,可以通过以下方式进行单向发送:

      <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>5.1.3</version> <!-- 替换为RocketMQ版本号 -->
        </dependency>
        DefaultMQProducer producer = new DefaultMQProducer("producer_group");
        producer.setNamesrvAddr("127.0.0.1:9876");
        try {
            producer.start();
            Message message = new Message("topic", "tag", "Hello, RocketMQ!".getBytes());
            producer.sendOneway(message);
        } catch (MQClientException | RemotingException | InterruptedException e) {
            throw new RuntimeException(e);
        }
        producer.shutdown();
    }

消费者代码:

public class C1 {
    public static void main(String[] args) {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group");
        consumer.setNamesrvAddr("127.0.0.1:9876");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);  //从那里开始消费
        try {
            consumer.subscribe("topic", "*");  //订阅主题
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                    System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), list);
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            consumer.start();
            System.out.println("Consumer started");
        } catch (MQClientException e) {
            throw new RuntimeException(e);
        }
    }
}
3.1.2同步发送

RocketMQ的同步发送是指消息发送方发送消息后,会等待消息发送到Broker,并收到Broker返回的发送结果后才继续执行后续的代码逻辑。在同步发送模式下,发送方会阻塞等待,直到消息被成功发送到Broker并得到确认。

具体地,使用RocketMQ的同步发送方式需要调用DefaultMQProducer类的send(Message)方法,并设置SendResult sendResult = producer.send(message)来发送消息。在发送消息后,send()方法会阻塞等待直到收到发送结果并返回SendResult对象。

使用RocketMQ的同步发送具有以下特点:

  1. 阻塞等待:在消息发送过程中,发送方会一直等待直到消息发送成功或发送失败,并根据返回的SendResult对象来判断发送的结果。
  2. 异常处理:如果发送过程中出现异常,同步发送会抛出相应的异常,可以通过捕获异常来进行错误处理,例如重试或记录错误日志。
  3. 可靠性:同步发送方式提供较高的可靠性,因为发送方会阻塞等待消息成功发送到Broker,并根据返回结果确认是否成功发送。

需要注意的是,同步发送方式会等待Broker返回发送结果,可能会在性能上产生一定的开销,并且如果发送方在发送过程中出现延迟或网络故障,会导致发送方的代码在等待中被阻塞。因此,在一些对性能要求较高的场景,可以考虑使用异步发送方式来提高吞吐量和性能。

案例代码:

package com.draven.demo;

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.client.producer.SendStatus;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;

/**
 * Created by Draven on 2023/8/2
 */
public class RocketMQSyncProducer {
    public static void main(String[] args) {
        // 创建生产者实例
        DefaultMQProducer producer = new DefaultMQProducer("producer_group");

        // 设置NameServer的地址
        producer.setNamesrvAddr("127.0.0.1:9876");

        try {
            // 启动生产者实例
            producer.start();

            // 创建消息实例,指定主题、标签和消息内容
            Message message = new Message("topic", "tag", "Hello, RocketMQ!".getBytes());

            // 发送消息并获取发送结果
            SendResult result = producer.send(message);

            // 判断发送状态
            if (result.getSendStatus() == SendStatus.SEND_OK) {
                System.out.println("Message sent successfully. Message ID: " + result.getMsgId());
            } else {
                System.out.println("Failed to send message. Send status: " + result.getSendStatus());
            }
        } catch (MQClientException | RemotingException | InterruptedException | MQBrokerException e) {
            e.printStackTrace();
        } finally {
            // 关闭生产者实例
            producer.shutdown();
        }
    }
}
3.1.3异步发送

RocketMQ的异步发送是一种消息发送模式,即消息发送方在发送消息后立即返回,不等待消息发送到RocketMQ Broker的确认结果,而是通过回调函数来处理发送结果。

在异步发送模式下,消息发送方通过调用DefaultMQProducer类的send(Message, SendCallback)方法发送消息,并传入一个实现了SendCallback接口的回调函数对象。发送方发送消息后立即返回,并通过回调函数处理发送结果。

具体流程如下:

  1. 发送方调用RocketMQ生产者的API,使用异步发送方式发送消息。
  2. 发送方将消息发送到RocketMQ Broker,然后立即返回。
  3. RocketMQ Broker接收到消息后,将消息写入磁盘,并向发送方发送发送结果的确认。
  4. 发送方不需要等待Broker的确认,而是通过回调函数处理发送结果。

使用异步发送方式可以提高消息发送的吞吐量和响应性能,因为发送方不需要等待Broker的确认,并且可以在发送消息后立即处理其他业务逻辑。同时,通过回调函数可以处理发送结果,并进行适当的错误处理和重试。

需要注意的是,异步发送方式相比于同步发送,对于发送结果的处理更为复杂。需要实现SendCallback接口的回调函数来处理发送结果,并在回调函数中处理成功和异常情况。

异步发送方式适用于对发送延迟要求较低,但对吞吐量和性能有较高要求的场景。在使用异步发送时,需要根据实际需求来确定适当的处理方式,例如设置回调函数、超时时间、错误处理和重试策略等。

代码案例:

package com.draven.demo;

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.client.producer.SendStatus;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;

/**
 * Created by Draven on 2023/8/2
 */
public class RocketMQSyncProducer {
    public static void main(String[] args) {
        // 创建生产者实例
        DefaultMQProducer producer = new DefaultMQProducer("producer_group");

        // 设置NameServer的地址
        producer.setNamesrvAddr("127.0.0.1:9876");

        try {
            // 启动生产者实例
            producer.start();

            // 创建消息实例,指定主题、标签和消息内容
            Message message = new Message("topic", "tag", "Hello, RocketMQ!".getBytes());

            // 发送消息并获取发送结果
            SendResult result = producer.send(message);

            // 判断发送状态
            if (result.getSendStatus() == SendStatus.SEND_OK) {
                System.out.println("Message sent successfully. Message ID: " + result.getMsgId());
            } else {
                System.out.println("Failed to send message. Send status: " + result.getSendStatus());
            }
        } catch (MQClientException | RemotingException | InterruptedException | MQBrokerException e) {
            e.printStackTrace();
        } finally {
            // 关闭生产者实例
            producer.shutdown();
        }
    }
}

3.2 顺序消息

RocketMQ的顺序消息是指一种消息传递方式,它确保按照生产者发送消息的顺序进行消费。在某些业务场景中,消息的顺序非常重要,比如订单处理、流程管理等。顺序消息保证消费者按照与生产者发送消息的顺序相同的顺序进行消费处理。

RocketMQ通过以下机制来实现顺序消息:

  1. 生产者发送顺序消息:生产者在发送顺序消息时,可以将消息发送到同一个Topic的相同队列中。RocketMQ会根据消息的顺序键(Order Key)来确定消息应该发送到哪个队列。相同顺序键的消息会进入同一个队列,从而保证了消息的顺序性。

  2. 消费者按顺序消费消息:消费者在消费顺序消息时,需要以固定的消费组(Consumer Group)和固定的消费顺序来消费消息。RocketMQ会确保同一个队列内的消息按照发送顺序被消费。消费者可以使用顺序消费模式来保证消费者在不同的线程中或者不同的进程中按照消息的发送顺序处理消息。

需要注意的是,RocketMQ无法保证在所有情况下完全按照顺序消费消息。因为消息在发送过程中可能会发生延迟、重试、消息重发等情况,这些因素可能导致消息的顺序性被破坏。因此,在使用顺序消息时,需要根据具体的业务场景和需求来评估是否接受一定的顺序失序。

总结来说,RocketMQ的顺序消息是指通过将消息发送到同一个队列,并按照顺序键进行消息路由,以保证生产者发送消息的顺序与消费者消费消息的顺序一致。这为某些依赖消息顺序的应用场景提供了一种可靠的实现方式。

代码案例:

4.RocketMQ主动拉取和推动消费

4.1主动拉取消费(Pull Consumer):

主动拉取消费是指消费者主动向RocketMQ Broker拉取消息。消费者通过调用DefaultMQPullConsumer类提供的拉取消息的API,向Broker发送请求,主动拉取消息。主动拉取消费的流程如下:

  • 消费者首先向Broker发送请求,请求拉取指定主题、消息队列和偏移量的消息。
  • Broker在接收到消费者的拉取请求后,返回请求的消息列表。
  • 消费者接收到消息列表后,进行消息的处理和消费。

主动拉取消费模式下,消费者主动控制消息拉取的速率和频率。消费者需要根据自身的需求定时拉取消息,可以根据处理能力和负载情况灵活控制消费速度。主动拉取的优点是可以灵活控制消费的节奏和顺序,适用于对消息消费的控制较高的场景。

4.2推动消费(Push Consumer):

推动消费是指RocketMQ Broker将消息主动推送给消费者。推动消费的流程如下:

  • 在推送消费模式下,消费者通过创建DefaultMQPushConsumer类的实例,并指定主题和标签来注册消费者。
  • 消费者与Broker建立连接后,Broker会将消息主动推送给消费者。
  • 消费者接收到消息后,进行消息的处理和消费。

推动消费模式下,RocketMQ Broker主动将消息推送给消费者,由Broker控制消费速度和消费节奏。推动消费的优点是实时性较高,Broker可以根据消费者的处理能力和负载情况来调整消息的推送速度,确保尽可能快速地将消息传递给消费者。推动消费适用于实时性要求较高的场景。

需要注意的是,推动消费需要消费者与Broker之间保持长连接,以便消息可以及时推送。而主动拉取消费则是消费者主动向Broker发起拉取消息的请求,可以采用短连接的方式。

选择主动拉取消费还是推动消费取决于具体的业务需求和实际情况。主动拉取消费提供了更高的灵活性和控制能力,而推动消费提供了更高的实时性和及时性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值