2、RabbitMQ入门:RabbitMQ架构、安装与配置、.NET客户端库(RabbitMQ.Client)基础使用
好,咱们正式开始聊 RabbitMQ。说实话,消息队列这块,RabbitMQ 是我个人用得最多的中间件之一。它成熟、稳定,社区活跃,在 .NET 生态里也是首选。今天这一章,我带你把 RabbitMQ 的底裤扒开看看——架构怎么设计的,怎么装起来,以及 .NET 里怎么用代码跟它“对话”。
2.1 RabbitMQ 架构:核心概念与模型
先别急着装软件,咱们得先搞明白 RabbitMQ 到底是个什么玩意儿。说白了,它就是一个“邮局”。你(生产者)把信(消息)交给邮局,邮局根据地址(路由规则)把信送到收件人(消费者)手里。
这个模型里,有几个核心角色,我一个个说:
- 生产者(Producer):发消息的一方。就是那个写信的人。
- 消费者(Consumer):收消息的一方。就是收信的人。
- 队列(Queue):消息的“信箱”。消息在队列里排队,等着被消费。
- 交换机(Exchange):邮局的“分拣中心”。生产者把消息发给交换机,交换机根据规则把消息路由到不同的队列。
- 绑定(Binding):交换机和队列之间的“连接线”。它告诉交换机,什么样的消息该送到哪个队列。
- 虚拟主机(Virtual Host):相当于邮局里的不同“部门”。一个 RabbitMQ 服务器可以创建多个 vhost,每个 vhost 里可以有自己独立的交换机、队列、绑定。权限隔离用的,我建议你每个项目用一个 vhost。
核心流程:
生产者 → 交换机 → (根据绑定规则) → 队列 → 消费者
消息不会直接进队列,必须先经过交换机。这一点很多新手会搞混。
交换机有几种类型,我简单列一下,后面章节会细讲:
| 类型 | 路由规则 | 典型场景 |
|---|---|---|
| Direct | 完全匹配 Routing Key | 点对点,比如日志级别 |
| Topic | 通配符匹配 Routing Key | 按主题订阅,比如订单事件 |
| Fanout | 广播给所有绑定的队列 | 发布/订阅,比如系统通知 |
| Headers | 匹配消息的 Header 属性 | 复杂路由,用得少 |
我的经验: 刚开始用 RabbitMQ 时,我总喜欢用 Fanout 交换机,觉得简单。后来项目大了,发现 Topic 交换机才是真香——灵活,扩展性好。你想想看,以后加个新消费者,改个绑定规则就行,不用动生产者代码。
2.2 安装与配置:从零跑起来
安装 RabbitMQ 其实不复杂,但有几个坑。我踩过,所以给你指出来。
2.2.1 安装 Erlang
RabbitMQ 是用 Erlang 写的,所以得先装 Erlang。去官网下载对应你系统的版本就行。Windows 下直接 exe 安装,Linux 下用包管理器。
注意: Erlang 版本和 RabbitMQ 版本有对应关系。装错了启动不了。我曾经因为装了个最新的 Erlang,结果 RabbitMQ 不认,折腾了半天。建议去 RabbitMQ 官网看版本兼容表。
2.2.2 安装 RabbitMQ Server
同样去官网下载。Windows 下是 exe,Linux 下是 rpm 或 deb。装完后,服务会自动启动。你可以用浏览器访问 http://localhost:15672 看看管理界面出来没。
默认账号密码是 guest/guest。但注意,这个账号只能在 localhost 登录。远程访问的话,得新建一个用户。
2.2.3 启用管理插件
管理界面默认是启用的。如果没出来,手动启用一下:
rabbitmq-plugins enable rabbitmq_management
然后重启服务:
rabbitmq-service stop
rabbitmq-service start
小技巧: 我习惯在开发环境用 Docker 装 RabbitMQ,一条命令搞定:
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
这样管理界面和消息端口都暴露出来了,省事。
2.2.4 创建用户和虚拟主机
生产环境千万别用 guest 账号。我建议你创建一个专用用户:
rabbitmqctl add_user myuser mypassword
rabbitmqctl set_user_tags myuser administrator
rabbitmqctl add_vhost myvhost
rabbitmqctl set_permissions -p myvhost myuser ".*" ".*" ".*"
这样,你的应用就用 myuser 连接 myvhost,权限隔离,安全又清晰。
2.3 .NET 客户端库(RabbitMQ.Client)基础使用
好,环境搭好了,咱们开始写代码。.NET 下最常用的库就是 RabbitMQ.Client。NuGet 上直接装:
Install-Package RabbitMQ.Client
2.3.1 建立连接
第一步,创建连接工厂。说白了就是告诉客户端,RabbitMQ 服务器在哪,用什么账号登录。
var factory = new ConnectionFactory()
{
HostName = "localhost",
UserName = "myuser",
Password = "mypassword",
VirtualHost = "myvhost"
};
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
这里要注意,Connection 是长连接,Channel 是轻量级的“会话”。一个连接可以创建多个 Channel。我建议你复用 Connection,但每个线程或任务用独立的 Channel。
避坑指南: 我曾经在项目里每个请求都创建新的 Connection,结果 RabbitMQ 服务器连接数暴涨,直接 OOM 了。记住,Connection 是昂贵的资源,一定要复用。
2.3.2 声明队列和交换机
在发消息之前,得先声明队列和交换机。如果队列不存在,RabbitMQ 会自动创建。
// 声明一个队列
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
// 声明一个交换机
channel.ExchangeDeclare(exchange: "my_exchange",
type: ExchangeType.Direct);
durable 参数表示队列是否持久化。如果设为 true,RabbitMQ 重启后队列还在。生产环境我建议都设为 true,除非你明确知道自己在做什么。
2.3.3 发送消息
发消息很简单,把消息转成字节数组,然后发布到交换机。
string message = "Hello, RabbitMQ!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "my_exchange",
routingKey: "hello",
basicProperties: null,
body: body);
Console.WriteLine($" [x] Sent {message}");
这里 routingKey 就是队列的名字(如果是 Direct 交换机)。如果是 Fanout 交换机,routingKey 会被忽略。
2.3.4 消费消息
消费者这边,需要订阅队列,然后处理消息。
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($" [x] Received {message}");
// 手动确认消息已处理
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
};
channel.BasicConsume(queue: "hello",
autoAck: false,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
我的习惯: 我一般把 autoAck 设为 false,然后手动调用 BasicAck。这样能确保消息处理成功后才从队列里移除。如果处理失败,消息会重新入队,不会丢。你想想看,如果自动确认,消费者挂了,消息就丢了,多坑。
2.3.5 完整示例:生产者 + 消费者
把上面代码拼起来,就是一个完整的“Hello World”了。我建议你动手跑一遍,感受一下消息从发到收的完整流程。
// 生产者
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.QueueDeclare("hello", durable: false, exclusive: false, autoDelete: false, arguments: null);
string message = "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "", routingKey: "hello", basicProperties: null, body: body);
Console.WriteLine($" [x] Sent {message}");
// 消费者(另一个控制台应用)
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.QueueDeclare("hello", durable: false, exclusive: false, autoDelete: false, arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($" [x] Received {message}");
};
channel.BasicConsume(queue: "hello", autoAck: true, consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
注意: 生产者和消费者要分别运行在两个控制台应用里。别在一个进程里又发又收,那样看不出消息队列的异步效果。
2.4 本章小结
这一章,咱们把 RabbitMQ 的架构、安装、以及 .NET 客户端的基础用法都过了一遍。说白了,就是搞清楚了“邮局”怎么运作,怎么搭起来,以及怎么用 C# 寄信和收信。
下一章,我会带你深入交换机类型和路由策略。到时候你会发现,RabbitMQ 的灵活之处远不止于此。嗯,先消化一下今天的内容,动手跑跑代码,有问题随时翻回来看看。
公众号:蓝海资料掘金营,微信deep3321