- 原文地址:RabbitMQ Tutorial - Routing
- 原文作者:RabbitMQ
- 译文出自:掘金翻译计划
路由选择
原文链接
(使用 amqp.node 客户端)
再上一节的教程中,我们搭建了一个简易的日志系统。我们可以向许多接收者广播日志消息。
在这一节的教程中,我们准备往里面添加一些功能——我们准备让它可以仅仅订阅消息的子集。例如,我们将可以把关键的错误消息直接指向日志文件(为了节省磁盘空间),然而却仍然可以在控制台上输入打印所有的日志消息。
连接器
在之前的例子中,我们已经创建了连接器。你可以这样调用代码:
channel.bindQueue(q.queue, exchange, '');
一个连接器是一个交换器和一个队列之间的关系。你可以这样简单的理解为:队列对来此这个交换器的消息感兴趣。
连接器可以携带一个额外的绑定键的参数(也就是上面代码中的空字符串)。这也就是我们怎么使用一个键来创建一个连接:
channel.bindQueue(queue_name, exchange_name, 'black');
连接键的含义取决于交换器的类型。这个fanout
交换器,在我们之前的教程中是会忽略它的值的。
Direct Exchange
我们上一节教程中的日志系统是会广播所有的消息给所有的消费者。我们想要去扩展,让消息基于严重程度允许进行消息的过滤。举个例子,我们想要写日志消息到磁盘上的脚本仅仅只接收严重的错误,并不会在警告或者信息级日志消息上浪费磁盘空间。
我们之前使用的是fanout
交换器,但没有给我们太多的灵活性——它只会盲目广播。
我们将会使用direct
交换器来代替它。direct
交换器背后的路由算法是简单的——一条消息只会走到那些值为binding key
的队列并和值为routing key
的消息完全匹配才行。
为了说明这个,来看一下下面的图:
在上图中,我们看到direct
交换器X
有着两个连接着的队列。第一个队列是由连接键为orange
连接的,第二个有两个连接键,一个连接键是black
,还有一个是green
。
在上图的结构中,一条带有路由键orange
的发布到交换器的消息将会被路由到队列Q1
。带有black
或者green
路由键的消息将会走到Q2
。所有其他的消息将会被丢弃。
Multiple bindings
使用相同的连接键来绑定多个队列是完全合法的。在我们的例子中,我们可以在X
和Q1
之间添加一个带有连接键black
的连接。在这种情况下,direct
交换器的行为将会和fanout
交换器的行为很像,并且会给所有匹配的队列广播消息。一条带有black
路由键的消息将会被发送到Q1
和Q2
两个队列。
发出日志(Emitting logs)
我们将会把这个模型使用在我们的日志系统上。取而代之,我们将会使用direct
交换器,而不是fanout
交换器。我们将提供日志严重性来作为routing key
。这种方式下接收脚本可以知道如何去选择它想要接收的严重程度的消息。让我们先专注于发出日志。
一如既往,我们需要先创建交换器:
var exchange = 'direct_logs';
channel.assertExchange(exchange, 'direct', {
durable: false
});
并且我们已经准备好发送一条消息:
var exchange = 'direct_logs';
channel.assertExchange(exchange, 'direct', {
durable: false
});
channel.publish(exchange, severity, Buffer.from(msg));
为了简化理解,我们假设severity
只有以下三种情况:info
,warning
,error
。
订阅(Subscribing)
接收消息的代码还是和之前的教程差不多,但是有一个地方不一样——我们将位我们感兴趣的每一个严重程度创建一个新的连接。
args.forEach(function(severity) {
channel.bindQueue(q.queue, exchange, severity);
});
把他们合到一起(Putting it all together)
emit_log_direct.js
脚本的代码如下:
#!/usr/bin/env node
var amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(error0, connection) {
if (error0) {
throw error0;
}
connection.createChannel(function(error1, channel) {
if (error1) {
throw error1;
}
var exchange = 'direct_logs';
var args = process.argv.slice(2);
var msg = args.slice(1).join(' ') || 'Hello World!';
var severity = (args.length > 0) ? args[0] : 'info';
channel.assertExchange(exchange, 'direct', {
durable: false
});
channel.publish(exchange, severity, Buffer.from(msg));
console.log(" [x] Sent %s: '%s'", severity, msg);
});
setTimeout(function() {
connection.close();
process.exit(0)
}, 500);
});
receive_logs_direct.js
脚本的代码如下:
#!/usr/bin/env node
var amqp = require('amqplib/callback_api');
var args = process.argv.slice(2);
if (args.length == 0) {
console.log("Usage: receive_logs_direct.js [info] [warning] [error]");
process.exit(1);
}
amqp.connect('amqp://localhost', function(error0, connection) {
if (error0) {
throw error0;
}
connection.createChannel(function(error1, channel) {
if (error1) {
throw error1;
}
var exchange = 'direct_logs';
channel.assertExchange(exchange, 'direct', {
durable: false
});
channel.assertQueue('', {
exclusive: true
}, function(error2, q) {
if (error2) {
throw error2;
}
console.log(' [*] Waiting for logs. To exit press CTRL+C');
args.forEach(function(severity) {
channel.bindQueue(q.queue, exchange, severity);
});
channel.consume(q.queue, function(msg) {
console.log(" [x] %s: '%s'", msg.fields.routingKey, msg.content.toString());
}, {
noAck: true
});
});
});
});
如果你想仅仅只保存warning
和error
(不包含info
)的日志消息,只需要打开终端并输入:
./receive_logs_direct.js warning error > logs_from_rabbit.log
如果你想要在屏幕上看所有的日志消息,打开一个新的终端并且输入:
./receive_logs_direct.js info warning error
# => [*] Waiting for logs. To exit press CTRL+C
举例,如果你想要发出日志消息只需要输入:
./emit_log_direct.js error "Run. Run. Or it will explode."
# => [x] Sent 'error':'Run. Run. Or it will explode.'
emit_log_direct.js 源码和receive_logs_direct.js——所有的源码
继续前进到教程5来了解如何基于一个模式来监听消息。