RabbitMQ 基础与实战:核心概念与 Hello World

各位同学,今天我们来聊聊消息队列。说实话,我在做后端开发的头两年,一直觉得消息队列是个「高大上」的东西。直到有一次,我负责的系统在双十一期间被瞬间流量打垮,数据库连接池直接爆掉……嗯,从那以后,我再也不敢轻视消息队列了。

RabbitMQ 是我个人用得最多的消息中间件。它稳定、成熟,社区活跃。今天这一章,我们就把它彻底搞明白。

RabbitMQ 核心概念:三驾马车

RabbitMQ 的核心,说白了就三个东西:Exchange(交换机)、Queue(队列)、Binding(绑定)。你想想看,消息从生产者发出,到消费者接收,中间经历了什么?

生产者把消息发给 Exchange,Exchange 根据 Binding 规则,把消息路由到对应的 Queue,消费者再从 Queue 里取消息。就这么简单。

核心流程:

Producer → Exchange → (Binding) → Queue → Consumer

我在项目中遇到过不少新手,上来就直接往 Queue 里发消息,跳过了 Exchange。这其实是不对的。RabbitMQ 的设计哲学就是:消息永远先到 Exchange,再由它决定去哪。

Exchange(交换机)

Exchange 是消息的「路由器」。它不存消息,只负责转发。RabbitMQ 有四种 Exchange 类型:

类型 路由规则 典型场景
Direct 完全匹配 Routing Key 点对点通信
Topic 通配符匹配 Routing Key 按主题分发
Fanout 广播给所有绑定的 Queue 发布/订阅
Headers 匹配消息的 Header 属性 复杂路由

我个人习惯,90% 的场景用 Direct 或 Topic 就够了。Fanout 适合做广播,比如配置更新通知。Headers 用得少,性能也差一些,能不碰就不碰。

Queue(队列)

Queue 是消息的「暂存区」。消息到了 Queue 之后,就等着消费者来取。Queue 有几个关键属性:

  • Durable:持久化。队列和消息都会写入磁盘,RabbitMQ 重启后不丢失。我建议生产环境一律设为 true。
  • Exclusive:排他。只被一个连接使用,连接断开自动删除。适合临时队列。
  • Auto-delete:自动删除。最后一个消费者断开后删除队列。

小提示:我曾经在生产环境踩过一个坑——队列没设 Durable,结果 RabbitMQ 重启后队列全没了,消息全部丢失。从那以后,我所有队列都加上了 durable=true。

Binding(绑定)

Binding 就是 Exchange 和 Queue 之间的「连接线」。它定义了 Exchange 把消息发给哪个 Queue,以及用什么规则匹配。

Binding 的核心是 Routing Key。比如你有一个 Direct 类型的 Exchange,Binding 时指定了 Routing Key 为 "order.create",那么只有携带 "order.create" 这个 Routing Key 的消息才会被路由到这个 Queue。

安装与配置

安装 RabbitMQ 其实很简单。我推荐用 Docker,省心省力。

# 拉取镜像并启动
docker run -d --name rabbitmq \
  -p 5672:5672 -p 15672:15672 \
  rabbitmq:3.12-management

5672 是 AMQP 协议端口,15672 是管理控制台端口。启动后,浏览器访问 http://localhost:15672,默认用户名密码都是 guest。

注意:生产环境千万别用 guest 账号。guest 只能在 localhost 登录。我建议创建一个专用用户,并设置强密码。

如果你不想用 Docker,直接安装也很简单:

# Ubuntu / Debian
sudo apt-get install rabbitmq-server

# macOS
brew install rabbitmq

安装完成后,启动服务:

sudo systemctl start rabbitmq-server
sudo systemctl enable rabbitmq-server  # 开机自启

Java 客户端连接与 Hello World

Java 客户端我用的是官方的 amqp-client。先加依赖:

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.20.0</version>
</dependency>

然后写一个最简单的生产者:

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;

public class Send {
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {
        // 1. 创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        
        // 2. 创建连接和通道
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            
            // 3. 声明队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            
            // 4. 发送消息
            String message = "Hello World!";
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println(" [x] Sent '" + message + "'");
        }
    }
}

消费者也很简单:

import com.rabbitmq.client.*;

public class Recv {
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        System.out.println(" [*] Waiting for messages...");
        
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
        };
        
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});
    }
}

先运行 Send,再运行 Recv,你就能看到消息被成功发送和接收了。

Python 客户端连接与 Hello World

Python 客户端我用 pika。先安装:

pip install pika

生产者代码:

import pika

# 1. 建立连接
connection = pika.BlockingConnection(
    pika.ConnectionParameters('localhost'))
channel = connection.channel()

# 2. 声明队列
channel.queue_declare(queue='hello')

# 3. 发送消息
channel.basic_publish(exchange='',
                      routing_key='hello',
                      body='Hello World!')
print(" [x] Sent 'Hello World!'")

connection.close()

消费者代码:

import pika

def callback(ch, method, properties, body):
    print(f" [x] Received {body}")

connection = pika.BlockingConnection(
    pika.ConnectionParameters('localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello')

channel.basic_consume(queue='hello',
                      auto_ack=True,
                      on_message_callback=callback)

print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

Python 的代码比 Java 简洁不少。不过要注意,pika 的 BlockingConnection 是同步的,生产环境建议用异步的 SelectConnection。

知识体系图

下面这张图,帮你把今天讲的内容串起来:

RabbitMQ 核心知识体系 Producer 发送消息 Exchange Direct / Topic / Fanout 路由消息 Binding 路由规则 Queue 暂存消息 Durable / 持久化 消费 Consumer 核心流程:Producer → Exchange → (Binding) → Queue → Consumer 四种 Exchange:Direct(精确匹配)、Topic(通配符)、Fanout(广播)、Headers(属性匹配)

避坑指南

最后,分享几个我踩过的坑:

  • 连接泄漏:我曾经在循环里创建 Connection 但不关闭,结果 RabbitMQ 的连接数飙到几千,直接 OOM。记住:Connection 是长连接,一个应用一个就够了。
  • 消息丢失:默认情况下,RabbitMQ 的消息是存在内存里的。一旦宕机,全丢。生产环境一定要开启持久化(Delivery Mode = 2)。
  • 队列名重复:不同业务用同一个队列名,会导致消息混乱。我习惯用「业务名_队列名」的命名方式,比如 order_create_queue。

我的建议:刚开始学 RabbitMQ,别急着搞复杂路由。先用 Direct Exchange + 一个 Queue 跑通 Hello World,再慢慢加功能。一口吃不成胖子,消息队列也一样。

好了,这一章的内容就到这里。RabbitMQ 的核心概念、安装配置、Java 和 Python 的 Hello World,你都应该能跑通了。下一章我们会深入消息的确认机制和持久化策略,到时候再聊。

可可精品资料库