RabbitMQ三:Publish/Subscribe
Exchanges(交换机)
RabbitMQ的消息发送模型核心思想是生产者不直接把消息发送到消息队列中。事实上,生产者不知道自己的消息将会被缓存到哪个队列中。
生产者只像交换机发送消息。一方面,它接收来自生产者的消息,另一方面,将它们推送到队列。交换机必须确切知道如何处理收到的消息。它应该附加到特定队列吗?它应该附加到许多队列吗?或者它应该被丢弃。其规则由交换类型定义 。
交换机有以下几种:direct
, topic
, headers
fanout
创建一个扇出(fanout
)类型交换机:
channel.ExchangeDeclare(“logs”,“fanout”);
扇出交换非常简单。它只是将收到的所有消息广播到它知道的所有队列中。
列出所有交换机
sudo rabbitmqctl list_exchanges
默认交换机
前几部分中,我们对交换一无所知,但仍能够向队列发送消息。这是可能的,因为我们使用的是默认交换,我们通过空字符串(”“)来识别。
channel.BasicPublish(exchange: "", routingKey: "task_queue", basicProperties:properties, body: body);
第一个参数是交换的名称。空字符串表示默认或无名交换:消息被路由到具有routingKey指定名称的队列(如果存在)。
临时队列
之前的示例中都是指定名称的队列(hello
,task_queue
),对应的生产者和消费者之间都要使用相同的消息队列名称。
但是本文中使用的日志系统却不是这样,我们希望了解所有日志消息,而不仅仅是它们的一部分。我们也只对目前流动的消息感兴趣,而不是旧消息。要解决这个问题,我们需要两件事:
- 每当我们连接到
Rabbit
时,我们都需要一个新的空队列。为此,我们可以使用随机名称创建队列,或者让服务器为我们选择随机队列名称。 - 一旦我们断开消费者,就应该自动删除队列。
在.NET客户端中,当我们不向 QueueDeclare()
提供参数时,我们使用生成的名称创建一个非持久的,独占的自动删除队列:
var queueName = channel.QueueDeclare().QueueName;
此时,queueName包含一个随机队列名称,它可能看起来像amq.gen-JzTY20BRgKO-HjmUJj0wLg
。
绑定
我们已经创建了一个扇出交换和一个队列。现在我们需要告诉交换机将消息发送到我们的队列。交换和队列之间的关系称为绑定。
channel.QueueBind(queue: queueName,
exchange: "logs",
routingKey: "");
从现在开始,日志交换会将消息附加到我们的队列中。
列出所有绑定
rabbitmqctl list_bindings
示例
EmitLog
class EmitLog
{
static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "127.0.0.1" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "logs", type: "fanout");
var message = GetMessage(args);
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "logs", routingKey: "", basicProperties: null, body: body);
Console.WriteLine($" [x] sent {message}");
}
Console.WriteLine($" Press [enter] to exit.");
Console.ReadLine();
}
private static string GetMessage(string[] args)
{
return ((args.Length > 0) ? string.Join(" ", args) : "info: Hello World!");
}
}
ReceiveLog
class ReceiveLog
{
static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "127.0.0.1" };
using (var conncetion = factory.CreateConnection())
using (var channel = conncetion.CreateModel())
{
channel.ExchangeDeclare(exchange: "logs", type: "fanout");
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName,
exchange: "logs",
routingKey: "");
Console.WriteLine($" [*] Waiting for logs");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($" [x] {message}");
};
channel.BasicConsume(queue: queueName,
autoAck: true,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}