介绍
先决条件
本教程假设 RabbitMQ 已安装并运行在标准端口( 5672 )上的本地主机上。如果您使用不同的主机、端口或凭据,则需要调整连接设置。
RabbitMQ 是一个消息代理:它接受和转发消息。您可以将其视为邮局:当您将要投递的邮件放入邮箱时,您可以确定信件承运人最终会将邮件递送给您的收件人。在这个比喻中,RabbitMQ 是一个邮箱、一个邮局和一个信件载体。
RabbitMQ 和邮局之间的主要区别在于它不处理纸张,而是接受、存储和转发二进制数据 blob消息。
RabbitMQ 和一般的消息传递使用了一些行话。
-
生产无非就是发送。发送消息的程序是生产者:
-
队列是位于 RabbitMQ 中的邮箱的名称。尽管消息流经 RabbitMQ 和您的应用程序,但它们只能存储在队列中。甲队列仅由主机的存储器&磁盘限制约束,它本质上是一个大的消息缓冲器。许多生产者可以将消息发送到一个队列,许多消费者可以尝试从一个队列接收数据。这是我们表示队列的方式:
-
消费与接收具有相似的含义。一个消费者是一个程序,主要是等待接收信息:
请注意,生产者、消费者和代理不必驻留在同一主机上;实际上在大多数应用程序中它们没有。应用程序也可以既是生产者又是消费者。
在本教程的这一部分,我们将用rabbitmq-delphi编写个小程序;发送单个消息的生产者(发送者),消息传递的是“Hello, World”。
在下图中,“P”是我们的生产者,“C”是我们的消费者。中间的盒子是一个队列——RabbitMQ 代表消费者保留的消息缓冲区。
我们的整体设计如下:
生产者将消息发送到Test.Q队列。
RabbitMQ 库
RabbitMQ 使用多种协议。本教程使用 AMQP 0-9-1,这是一种用于消息传递的开放式通用协议。RabbitMQ 有许多不同语言的客户端。在本教程系列中,我们将使用rabbitmq-delphi 1.0.0,这是 本人翻译自rabbitmq-c的 客户端。
function main:integer;
var
port, status : integer;
ret : Tamqp_rpc_reply;
socket : Pamqp_socket;
conn : Pamqp_connection_state;
props : Tamqp_basic_properties;
hostname, routingkey, exchange, messagebody: PAnsiChar;
vl: Tva_list;
begin
socket := nil;
hostname := 'localhost';
port := 5672;
exchange := 'Test.EX';
routingkey := 'test-key';
messagebody := 'Hello, World!';
if initializeWinsockIfNecessary() < 1 then
begin
Writeln('Failed to initialize "winsock": ');
Exit;
end;
conn := amqp_new_connection();
socket := amqp_tcp_socket_new(conn);
if not Assigned(socket) then begin
Writeln('creating TCP socket failed!');
Exit;
end;
status := amqp_socket_open(socket, hostname, port);
if status > 0 then begin
Writeln('opening TCP socket failed!');
Exit;
end;
vl.username := 'sa';
vl.password := 'admin';
vl.identity := '';
die_on_amqp_error(amqp_login(conn, '/', 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, vl),
'Logging in');
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), 'Opening channel');
begin
props._flags := AMQP_BASIC_CONTENT_TYPE_FLAG or AMQP_BASIC_DELIVERY_MODE_FLAG;
props.content_type := amqp_cstring_bytes('text/plain');
props.delivery_mode := 2; { persistent delivery mode }
amqp_basic_publish(conn, 1, amqp_cstring_bytes(exchange),
amqp_cstring_bytes(routingkey), 0, 0,
@props, amqp_cstring_bytes(messagebody));
end;
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
'Closing channel');
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
'Closing connection');
die_on_error(amqp_destroy_connection(conn), 'Ending connection');
end;
begin
try
Main;
except
on e:Exception do
WriteLn(e.Message);
end;
end.
我们现在连接到本地机器上的代理 - 因此是 localhost。如果我们想连接到另一台机器上的代理,我们只需在此处指定其名称或 IP 地址。
接下来,在发送之前,我们需要确保接收方队列存在。如果我们向不存在的位置发送消息,RabbitMQ 只会丢弃该消息。
此时我们已准备好发送消息。我们的第一条消息将只包含一个字符串Hello, World!我们想将它发送到我们的Test.Q队列。
在 RabbitMQ 中,消息永远不能直接发送到队列,它总是需要经过exchange。
在退出程序之前,我们需要确保网络缓冲区已刷新并且我们的消息实际上已传递到 RabbitMQ。我们可以通过轻轻关闭连接来实现。