4、RabbitMQ入门:核心概念与安装配置

好,咱们开始聊 RabbitMQ。说实话,消息队列这块,RabbitMQ 是我个人用得最多的中间件之一。它稳定、成熟,社区活跃,非常适合游戏服务器这种对可靠性要求高的场景。

这一章,咱们先把 RabbitMQ 的底子打牢。我会带你搞懂三个核心概念:Exchange、Queue、Binding。然后手把手把环境搭起来。嗯,别急,一步步来。

4.1 核心概念:Exchange、Queue、Binding

这三个东西,说白了就是 RabbitMQ 的骨架。你想想看,消息从生产者发出来,到消费者收到,中间经历了什么?

  • Queue(队列):消息的“收件箱”。消息最终都存在这里,等着消费者来取。
  • Exchange(交换机):消息的“路由器”。生产者不直接把消息扔进队列,而是扔给 Exchange。Exchange 根据规则,决定把消息投递到哪个或哪些队列。
  • Binding(绑定):连接 Exchange 和 Queue 的“规则线”。它告诉 Exchange:“嘿,如果消息符合这个条件,就把它送到这个队列去。”

我在项目中遇到过不少新手,上来就以为生产者直接发消息到队列。其实不是的。RabbitMQ 的设计哲学是:生产者只跟 Exchange 打交道,消费者只跟 Queue 打交道。这个解耦非常巧妙。

4.1.1 Queue(队列)

队列就是消息的容器。它有几个关键属性:

  • 名称:每个队列有个唯一名字。不指定的话,RabbitMQ 会自动生成一个。
  • 持久化:如果设为 durable=true,RabbitMQ 重启后队列还在。我建议生产环境一律开持久化,别省这点性能。
  • 排他性exclusive=true 表示这个队列只被当前连接使用,连接断开就自动删除。临时队列常用这个。
  • 自动删除auto-delete=true 表示最后一个消费者断开后,队列自动删除。

避坑指南:我曾经在测试环境忘了关自动删除,结果调试到一半,消费者一断开,队列没了,消息全丢了。排查了半天才发现是 auto-delete 搞的鬼。所以,生产环境务必明确设置这些参数。

4.1.2 Exchange(交换机)

Exchange 是 RabbitMQ 的大脑。它收到消息后,根据类型和绑定规则,决定消息的去向。常见的 Exchange 类型有四种:

类型 路由规则 典型场景
Direct 消息的 routing key 与队列绑定的 routing key 完全匹配 点对点通信,比如玩家私聊
Topic routing key 支持通配符匹配(* 匹配一个词,# 匹配零个或多个词) 按主题分发,比如服务器日志分类
Fanout 忽略 routing key,把消息广播到所有绑定的队列 广播通知,比如全服公告
Headers 根据消息的 header 属性匹配,而不是 routing key 复杂路由条件,用得较少

我个人习惯,游戏服务器里最常用的是 DirectTopic。Fanout 偶尔用在跨服广播上。Headers 嘛,说实话,我做了这么多年游戏,几乎没用过。

4.1.3 Binding(绑定)

Binding 就是一条规则。它把 Exchange 和 Queue 连起来,并指定一个 routing key。举个例子:

# 创建一个名为 "player_queue" 的队列
# 绑定到 "game_exchange" 交换机,routing key 为 "player.login"
channel.queueBind("player_queue", "game_exchange", "player.login");

这条绑定意味着:当消息发到 game_exchange,并且 routing key 是 player.login 时,这条消息就会被投递到 player_queue

小技巧:一个队列可以绑定多个 routing key,也可以绑定到多个 Exchange。反过来,一个 Exchange 也可以绑定多个队列。这种多对多的关系,让消息路由变得非常灵活。

4.2 安装与配置

理论说完了,咱们动手装一个。我以 CentOS 7 为例,Windows 和 Mac 的安装方式大同小异,官网都有文档。

4.2.1 安装 Erlang

RabbitMQ 是用 Erlang 写的,所以得先装 Erlang。注意版本匹配,别装错了。我建议直接用 RabbitMQ 官方提供的 Erlang 包。

# 添加 Erlang 仓库
wget https://packages.erlang-solutions.com/erlang-solutions-2.0-1.noarch.rpm
rpm -Uvh erlang-solutions-2.0-1.noarch.rpm

# 安装 Erlang
yum install -y erlang

# 验证安装
erl -version

嗯,这里要注意:Erlang 版本和 RabbitMQ 版本有对应关系。装之前去 RabbitMQ 官网查一下兼容性列表,省得后面出幺蛾子。

4.2.2 安装 RabbitMQ

# 下载 RabbitMQ 的 RPM 包
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.12.0/rabbitmq-server-3.12.0-1.el7.noarch.rpm

# 安装
rpm -ivh rabbitmq-server-3.12.0-1.el7.noarch.rpm

# 启动服务
systemctl start rabbitmq-server

# 设置开机自启
systemctl enable rabbitmq-server

# 查看状态
systemctl status rabbitmq-server

警告:安装过程中如果报依赖缺失,比如 socat,直接用 yum install -y socat 装上就行。我曾经在一台最小化安装的服务器上踩过这个坑,折腾了半小时。

4.2.3 启用管理插件

RabbitMQ 自带一个 Web 管理界面,非常方便。我个人习惯一装好就把它打开。

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

# 重启服务
systemctl restart rabbitmq-server

然后打开浏览器,访问 http://你的服务器IP:15672。默认用户名密码都是 guest。不过注意,guest 用户只能在 localhost 登录。远程访问的话,得新建一个用户。

4.2.4 创建用户与虚拟主机

生产环境里,我建议为每个项目或每个服务创建独立的用户和虚拟主机(vhost)。vhost 相当于 RabbitMQ 里的命名空间,不同 vhost 之间的 Exchange、Queue 完全隔离。

# 创建用户
rabbitmqctl add_user game_user game_password

# 设置用户标签(管理员权限)
rabbitmqctl set_user_tags game_user administrator

# 创建虚拟主机
rabbitmqctl add_vhost game_vhost

# 授权用户访问虚拟主机
rabbitmqctl set_permissions -p game_vhost game_user ".*" ".*" ".*"

这三条 ".*" 分别代表:配置权限、写权限、读权限。生产环境可以按需收紧,比如只给某个用户读某个队列的权限。

避坑指南:我曾经在项目初期图省事,所有服务共用一个 vhost 和一个 guest 用户。结果后来微服务一多,队列和交换机乱成一锅粥,排查问题非常痛苦。所以,从一开始就做好隔离,后面会省心很多。

4.3 验证安装

装完了,咱们写个简单的 Java 代码验证一下。这里用 RabbitMQ 的 Java 客户端。

// 生产者:发送一条消息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setUsername("game_user");
factory.setPassword("game_password");
factory.setVirtualHost("game_vhost");

try (Connection connection = factory.newConnection();
     Channel channel = connection.createChannel()) {
    
    // 声明一个队列
    channel.queueDeclare("hello_queue", true, false, false, null);
    
    // 发送消息
    String message = "Hello RabbitMQ!";
    channel.basicPublish("", "hello_queue", null, message.getBytes());
    System.out.println(" [x] Sent '" + message + "'");
}
// 消费者:接收消息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setUsername("game_user");
factory.setPassword("game_password");
factory.setVirtualHost("game_vhost");

try (Connection connection = factory.newConnection();
     Channel channel = connection.createChannel()) {
    
    channel.queueDeclare("hello_queue", true, false, false, null);
    
    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
        String message = new String(delivery.getBody(), "UTF-8");
        System.out.println(" [x] Received '" + message + "'");
    };
    
    channel.basicConsume("hello_queue", true, deliverCallback, consumerTag -> {});
    System.out.println(" [*] Waiting for messages...");
    
    // 让程序保持运行
    Thread.sleep(10000);
}

先跑生产者,再跑消费者。如果控制台打印出消息,说明 RabbitMQ 安装配置成功了。

小提示:代码里 queueDeclare 的第二个参数 true 表示队列持久化。生产环境一定要开。第三个参数 false 表示非排他,第四个 false 表示不自动删除。这四个参数,我建议你记牢,面试也常问。

4.4 本章小结

这一章,咱们把 RabbitMQ 的三大核心概念讲透了。Exchange 负责路由,Queue 负责存储,Binding 负责连接。安装配置也走了一遍,从 Erlang 到 RabbitMQ,再到管理插件和用户权限。

下一章,我会带你深入 Exchange 的四种类型,用实际代码演示它们的工作方式。到时候你会发现,理解了 Exchange,RabbitMQ 就算入门了。

好,先到这里。动手装一个试试,有问题咱们下章聊。