3、RabbitMQ入门:架构、安装与核心概念

各位同学,今天我们来聊聊消息队列里的老牌劲旅——RabbitMQ。说实话,我在做微服务架构的时候,RabbitMQ 一直是我的首选。为什么?因为它稳定、成熟,而且社区生态非常好。你想想看,一个从 2007 年活到现在的中间件,能没两把刷子吗?

3.1 RabbitMQ 的整体架构

先看一张架构图(嗯,我这里用文字描述,你们脑补一下)。RabbitMQ 的核心角色有三个:生产者Broker(消息代理)和消费者

生产者把消息扔给 Broker,Broker 负责存储和转发,消费者从 Broker 拉取消息。但这里有个关键点——生产者不是直接把消息发给队列的,而是发给 Exchange(交换机)。Exchange 再根据规则把消息路由到对应的 Queue(队列)

我个人习惯把 Exchange 想象成一个快递分拣中心。你寄快递时不会直接送到收件人手里,而是先送到分拣中心,分拣中心根据地址(Routing Key)把包裹分到不同的配送站(Queue),最后由配送员(消费者)送到你手上。

核心组件一览:

  • Producer:消息生产者,发送消息的一方
  • Consumer:消息消费者,接收消息的一方
  • Exchange:交换机,负责消息路由
  • Queue:队列,存储消息的地方
  • Binding:绑定,定义 Exchange 和 Queue 之间的关系
  • Virtual Host:虚拟主机,逻辑隔离,类似数据库的 schema

3.2 安装与启动

安装 RabbitMQ 其实很简单。我推荐用 Docker 方式,省心。当然,你也可以直接装原生版本。

3.2.1 Docker 安装(推荐)

# 拉取镜像(带管理界面的版本)
docker pull rabbitmq:3.12-management

# 启动容器
docker run -d --name rabbitmq \
  -p 5672:5672 \
  -p 15672:15672 \
  rabbitmq:3.12-management

这里注意一下,5672 是 AMQP 协议端口,给客户端用的。15672 是管理界面端口,给浏览器用的。我第一次装的时候只开了 5672,结果管理界面打不开,折腾了半天才发现少了个端口映射。嗯,这种坑踩过一次就记住了。

3.2.2 原生安装(Linux)

# Ubuntu/Debian
apt-get install rabbitmq-server

# CentOS/RHEL
yum install rabbitmq-server

# 启动服务
systemctl start rabbitmq-server
systemctl enable rabbitmq-server

# 启用管理插件
rabbitmq-plugins enable rabbitmq_management

启动后,访问 http://localhost:15672,默认用户名密码都是 guest。不过要注意,guest 用户只能在 localhost 登录,远程访问需要新建用户。

我曾经踩过的坑:生产环境千万别用 guest 用户!一定要创建专用用户并设置强密码。另外,记得配置防火墙,只开放需要的端口。

3.3 管理界面初探

打开管理界面,你会看到几个主要区域:

  • Overview:概览页,显示集群状态、节点信息、消息速率等
  • Connections:当前所有连接
  • Channels:每个连接下的通道
  • Exchanges:所有交换机列表
  • Queues:所有队列列表
  • Admin:用户管理、虚拟主机管理等

我个人习惯先看 Queues 页面。为什么?因为队列是消息的最终落脚点,队列的状态直接反映了系统的健康程度。如果队列堆积了大量消息,说明消费者处理不过来,或者消费者挂了。

小技巧:在管理界面里,你可以直接往队列里发消息测试,也可以查看消息内容。调试阶段特别有用。

3.4 核心概念详解

3.4.1 Exchange(交换机)

Exchange 是 RabbitMQ 的大脑。它决定了消息该往哪走。RabbitMQ 提供了四种交换机类型:

类型 路由规则 适用场景
Direct Routing Key 完全匹配 点对点通信,如日志级别
Topic Routing Key 模式匹配(支持通配符) 按主题分发,如日志分类
Fanout 广播到所有绑定的队列 广播消息,如配置更新
Headers 根据消息头属性匹配 复杂路由,较少使用

举个例子。我在项目中做过一个日志系统:

  • 使用 Direct Exchange,Routing Key 设为 infowarnerror
  • 消费者 A 绑定 error,只收错误日志
  • 消费者 B 绑定 infowarn,收普通日志
  • 这样实现了日志的按级别分流

3.4.2 Queue(队列)

队列就是消息的缓冲区。生产者发来的消息,在消费者处理之前,都老老实实待在队列里。

队列有几个重要属性:

  • Durable:持久化,重启后队列还在
  • Auto Delete:自动删除,最后一个消费者断开后删除队列
  • Exclusive:排他性,只被一个连接使用
  • Arguments:扩展参数,如消息 TTL、队列长度限制等

我的建议:生产环境一定要设置 Durable=true。否则 RabbitMQ 重启后队列就没了,消息也会丢失。我曾经因为忘记设置持久化,导致半夜重启后丢了一堆订单消息,被运维同事追着骂了一周。

3.4.3 Binding(绑定)

Binding 是连接 Exchange 和 Queue 的纽带。它告诉 Exchange:「当消息满足这个条件时,请把它送到这个队列。」

绑定的时候需要指定:

  • 绑定的 Exchange
  • 绑定的 Queue
  • Routing Key(对于 Direct 和 Topic 类型)

说白了,Binding 就是一条规则。没有 Binding,Exchange 和 Queue 就是孤立的,消息无处可去。

3.5 一个完整的示例

光说不练假把式。我们写个简单的 Go 程序,演示一下怎么用 RabbitMQ。

package main

import (
    "log"
    "github.com/streadway/amqp"
)

func main() {
    // 1. 连接 RabbitMQ
    conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
    if err != nil {
        log.Fatal("连接失败:", err)
    }
    defer conn.Close()

    // 2. 创建通道
    ch, err := conn.Channel()
    if err != nil {
        log.Fatal("创建通道失败:", err)
    }
    defer ch.Close()

    // 3. 声明交换机
    err = ch.ExchangeDeclare(
        "logs",      // 交换机名称
        "fanout",    // 类型
        true,        // 持久化
        false,       // 自动删除
        false,       // 内部使用
        false,       // 等待服务器确认
        nil,         // 参数
    )
    if err != nil {
        log.Fatal("声明交换机失败:", err)
    }

    // 4. 发送消息
    body := "Hello, RabbitMQ!"
    err = ch.Publish(
        "logs",      // 交换机
        "",          // Routing Key(fanout 类型忽略)
        false,       // 强制
        false,       // 立即
        amqp.Publishing{
            ContentType: "text/plain",
            Body:        []byte(body),
        },
    )
    if err != nil {
        log.Fatal("发送消息失败:", err)
    }

    log.Println("消息发送成功:", body)
}

这个例子很简单,但包含了核心流程:连接 → 创建通道 → 声明交换机 → 发送消息。你想想看,实际项目中无非就是在这个基础上加队列声明、绑定、消费者逻辑。

3.6 避坑指南

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

  • 连接泄漏:每次操作都新建连接,用完不关闭。一定要复用连接,用 Channel 做并发操作。
  • 消息丢失:没有设置消息持久化,或者没有确认机制。生产环境一定要用 Publisher ConfirmsConsumer Ack
  • 队列堆积:消费者处理速度跟不上生产者。可以设置队列长度限制,或者增加消费者数量。
  • 死信队列:消息被拒绝或者过期后,如果没有配置死信队列,消息就丢了。建议给每个业务队列配一个死信队列。

好了,这一章的内容就到这里。RabbitMQ 入门其实不难,关键是理解 Exchange、Queue、Binding 这三者的关系。下一章我们会深入消息的可靠投递,到时候再聊。