RabbitMQ.Client API (.NET)中文文档

主要的名称空间,接口和类

核心API中定义接口和类 RabbitMQ.Client 名称空间:

1
using RabbitMQ.Client;
核心API接口和类
  • IModel :表示一个AMQP 0-9-1频道,提供了大部分 的操作(方法)协议。
  • IConnection :表示一个AMQP 0-9-1连接
  • ConnectionFactory :构造 IConnection 实例
  • IBasicConsumer:代表一个消费者消息
其他有用的接口和类包括:
  • DefaultBasicConsumer:常用的消费者基类
RabbitMQ.Client以外的公共命名空间包括:
  • RabbitMQ.Client.Events :客户端库的各种事件和事件处理程序,包括 EventingBasicConsumer , 建立在消费者实现c#事件处理程序。
  • RabbitMQ.Client.Exceptions :用户可见的异常。

所有其他的命名空间是留给库的私有实现细节,虽然私有的命名空间的成员使用该库以允许开发者实现他们在库实现发现错误或设计错误的解决方法通常是提供给应用程序。应用程序可以不依赖任何类,接口,成员出现私人的命名空间内的跨库的版本保持稳定的变量等。

连接到代理(Connecting to a Broker

要连接到RabbitMQ的,有必要实例化(例示)一个连接工厂和其配置为使用所需的主机,虚拟主机和证书(证书)。然后使用ConnectionFactory.CreateConnection()打开的连接。下面两段代码连接到主机名RabbitMQ的节点:
  1. ConnectionFactory factory = new ConnectionFactory(); factory.UserName = user; // "gue
  2. factory.Password = pass;
  3. factory.VirtualHost = vhost;
  4. factory.HostName = hostName;
  5. IConnection conn = factory.CreateConnection();
  1. ConnectionFactory factory = new ConnectionFactory();
  2. factory.Uri = "amqp://user:pass@hostName:port/vhost";
  3. IConnection conn = factory.CreateConnection();
由于.NET客户端使用AMQP 0-9-1 URI规格比其他客户的严格的解释,必须小心使用URI时服用。特别是,主机部分不能被省略,并且与空名称虚拟主机不可寻址(可寻址的)。所有出厂的属性都有默认值。如果该属性保持建立连接之前未分配将用于一个属性的默认值: 用户名     “guest” 密码     “guest” 虚拟主机     “/” 主机名     “localhost” 端口     5672定期连接,5671连接使用TLS 然后IConnection接口可用于打开一个通道:
  1. IModel channel= conn.CreateModel();
通道现在可以被用来发送和接收消息,如在随后的章节中描述。
使用交换机(Exchanges)和队列(Queues)

客户端应用程序在交换机和队列中工作,AMQP 0-9-1的高层次的积木工作。这些都必须先“申明”,然后才能使用它们。声明任一类型的对象只是确保该名称的一个存在,如果有必要创造它。继续前面的例子,下面的代码声明一个交换机和队列,然后绑定在一起。
  1. model.ExchangeDeclare(exchangeName, ExchangeType.Direct);
  2. model.QueueDeclare(queueName, false, false, false, null);
  3. model.QueueBind(queueName, exchangeName, routingKey, null);
这将激活一下对象:
   1: “直连”(direct)类型的非持久性(non-durable),非自动删除(non-autodelete)交换机(exchange)
   2:非持久,不自动删除,非排他性(non-exclusive)的队列

       交换机可通过使用额外的参数定制。上面的代码将队列绑定到指定路由的交换机。请注意,许多通道API(IModel)方法被重载。 ExchangeDeclare的便利缩写形式使用合理的默认值。也有较长的形式与更多的参数,让你根据需要重载这些默认值,充分控制在需要的地方。这种“短版,长版”的格局在整个API使用。

发送消息(Publishing Messages)

使用 IModel.BasicPublish 发送消息到交换机, 如下:
  1. byte[] messageBodyBytes = System.Text.Encoding.UTF8.GetBytes("Hello, world!");
  2. model.BasicPublish(exchangeName, routingKey, null, messageBodyBytes);
为了更好的控制,可以使用重载变量指定的强制性标志,或指定的消息属性:
  1. byte[] messageBodyBytes = System.Text.Encoding.UTF8.GetBytes("Hello, world!");
  2. IBasicProperties props = model.CreateBasicProperties();
  3. props.ContentType = "text/plain";
  4. props.DeliveryMode = 2;
  5. model.BasicPublish(exchangeName,routingKey,props,messageBodyBytes);

以持续性的交互模式发送文本消息,有关消息属性的详细信息请查看IBasicProperties接口的定义。

在下面的例子中,我们发送定义了Header(头)的消息:

1
2
3
4
5
6
7
8
9
10
byte[] messageBodyBytes = System.Text.Encoding.UTF8.GetBytes("Hello, world!");
   
IBasicProperties props = model.CreateBasicProperties();
props.ContentType = "text/plain";
props.DeliveryMode = 2;
props.Headers = new Dictionary<string, object>();
props.Headers.Add("latitude",  51.5252949);
props.Headers.Add("longitude", -0.0905493);
   
model.BasicPublish(exchangeName, routingKey,props, messageBodyBytes);

下面的示例代码设置消息过期时间:

1
2
3
4
5
6
7
8
byte[] messageBodyBytes=System.Text.Encoding.UTF8.GetBytes("Hello, world!");
  
IBasicProperties props = model.CreateBasicProperties();
props.ContentType = "text/plain";
props.DeliveryMode = 2;
props.Expiration = "36000000"
  
mode.BasicPublish(exchangeName,routingKey,props,messageBodyBytes);

获取单条消息(Fetching Individual Messages ("pull API"))

使用IModel.BasicGet获取单条消息,从消息的Header(属性)和消息主体可以获取到BasicGetResult的实例

1
2
3
4
5
6
7
8
bool noAck = false;
BasicGetResult result = channel.BasicGet(queueName, noAck);
if (result == null) {
    // No message available at this time.
} else {
    IBasicProperties props = result.BasicProperties;
    byte[] body = result.Body;
    ...

上面的  noAck=false 你也可以使用  IModel.BasicAck 来确认成功的接受并处理了消息。

1
2
3
4
...
    // acknowledge receipt of the message
    channel.BasicAck(result.DeliveryTag, false);
}

注意:使用该API获取消息是效率较低。如果你想使用RabbitMQ将邮件推送到客户端,请参阅下一节。 


通过订阅检索消息(Retrieving Messages By Subscription ("push API"))


接收消息的另一种方法是使用IBasicConsumer接口建立订阅。该消息将在到达时被自动推送,而不必进行主动请求。实现消费者的一种方法是使用的方便(convenience)类EventingBasicConsumer,其中调度交付和其他消费的生命周期事件为C#事件:

1
2
3
4
5
6
7
8
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (ch, ea) =>
                {
                    var body = ea.Body;
                    // ... process the message
                    ch.BasicAck(ea.DeliveryTag, false);
                };
String consumerTag=channel.BasicConsume(queueName,false,consumer);

另一种选择是继承DefaultBasicConsumer类,重写必要的方法,或者直接实现IBasicConsumer。通常要实现核心方法IBasicConsumer.HandleBasicDeliver。更复杂的消费者将需要实施进一步的方法。特别是,HandleModelShutdown使 channel/connection关闭。消费者还可以实现HandleBasicCancelOk通知取消的消息。 在没有被提交给原始IModel.BasicConsume情况下,DefaultBasicConsumer的ConsumerTag属性可用于检索服务器生成的消费者标签。您可以使用IModel.BasicCancel主动取消消费者:

1
channel.BasicCancel(consumerTag);

当调用API方法,你总是通过消费者标签提交到他们自己的消费者,它可以是客户端或服务器生成的,详见AMQP规范0-9-1文件中解释。


消费者的并发性考虑(Concurrency Considerations for Consumers)


每个IConnection实例,在当前实现中,由单个后台线程从Socket中读取并调度所得事件给应用程序的支持。如果启用心跳,必须3.5.0版本,它们用.NET的定时器来实现的。通常,因此,在使用这种库的应用程序至少需要激活两个线程:

1:应用程序线程(the application thread

包含应用程序的逻辑,调用 IModel 的方法执行协议操作。

2:活动的 I/O 线程

通过IConnection的实例隐藏和完全管理


在任何 回调的应用程序和库中,线程模型对应用程序是可见的。这样的回调包括:

1、任何IBasicConsumer方法

2、在IModel的BasicReturn事件

3、任何对IConnection了各种关闭事件,IModel等。


消费者回调和订阅(Consumer Callbacks and Ordering)


从版本3.5.0应用回调处理程序可以调用阻塞操作(如IModel.QueueDeclare或IModel.BasicCancel)。IBasicConsumer回调并发调用。然而,每个通道的操作顺序将予以保留。换句话说,如果消息A和B在该顺序输送在同一通道上,它们将被以该顺序进行处理。如果消息A和B分别在不同的通道输送,它们可以以任何顺序进行处理(或并行)。消费者回调在派往由.NET运行库提供的默认的TaskScheduler任务调用。


使用自定义计划任务(Using a Custom Task Scheduler)

我们可以通过设置ConnectionFactory.TaskScheduler使用自定义的任务调度程序:

1
2
3
4
5
6
7
public class CustomTaskScheduler : TaskScheduler
{
  // ...
}
 
var cf = new ConnectionFactory();
cf.TaskScheduler = new CustomTaskScheduler();

此处的例子,可以用来限制与一个自定义的TaskScheduler并发程度。


线程之间共享通道(Sharing Channels Between Threads)

根据经验,IModel实例不应由多个线程同时使用:应用程序代码应该为IModel实例维护一个清晰的线程所有权概念。如果多个线程需要访问特定的IModel实例,应用程序应该实施互斥本身。 实现这一点的一种方式是对于IModel的所有用户锁定实例本身

  1. IModel ch = RetrieveSomeSharedIModelInstance();
  2. lock (ch) {
  3. ch.BasicPublish(...);
  4. }
不正确序列化的IModel操作包括但不限于,如下:

1、 在线路上发送的无效帧序列(例如,如果同时运行多于一个BasicPublish操作,则发生),和/或NotSupportedExceptions从RpcContinuationQueue类中的方法抛出,引发“禁止请求的管道”(在同时运行多个AMQP 0-9-1同步操作(如ExchangeDeclare)的情况下)。


处理不可路由的消息(Handling Unroutable Messages

如果发布的消息具有设置的“mandatory”标志,但不能传递,代理将返回给发送客户端(通过basic.return AMQP 0-9-1命令)。 为了通知这样的返回,客户可以订阅IModel.BasicReturn事件。 如果没有连接到事件的侦听器,则返回的消息将被静默删除。

  1. model.BasicReturn +=
  2. new RabbitMQ.Client.Events.BasicReturnEventHandler(...);
例如,如果客户端发布了一条“强制”标志设置为未绑定到队列的“direct”类型交换的消息,则BasicReturn事件将触发。

断开与RabbitMQ的连接(Disconnecting from RabbitMQ)

要断开连接,只需关闭通道和连接:

  1. channel.Close(200, "Goodbye");
  2. conn.Close();
注意,关闭频道被认为是良好的做法,但不是绝对必要的 - 它将在底层连接关闭时自动完成。 在某些情况下,您可能希望连接在连接上的最后一个打开通道关闭后自动关闭。 要实现这一点,请将IConnection.AutoClose属性设置为true,但仅在创建第一个通道后:
  1. IConnection conn = factory.CreateConnection(...);
  2. IModel channel = conn.CreateModel();
  3. conn.AutoClose = true;
当AutoClose为true时,最后关闭的通道也将导致连接关闭。 如果在创建任何通道之前将其设置为true,则连接将在此时关闭。

从网络故障自动恢复(Automatic Recovery From Network Failures)
连接恢复(Connection Recovery)

客户端和RabbitMQ节点之间的网络连接可能失败。 RabbitMQ .NET / C#客户端支持自动恢复连接和拓扑(queues, exchanges, bindings, and consumers)。 许多应用程序的自动恢复过程遵循以下步骤:

     1、重新连接 (Reconnect)

     2、还原连接侦听器( Restore connection listeners)

     3、重新打开通道(Re-open channels)

     4、还原频道侦听器(Restore channel listeners

     5、恢复通道basic.qos设置,发布者确认和事务设置( Restore channel basic.qos setting, publisher confirms and transaction settings


拓扑恢复包括对每个通道执行的以下操作:

     1、重新声明交易(除了预定义的交易)(Re-declare exchanges (except for predefined ones)

     2、重新声明队列(Re-declare queues)

     3、恢复所有绑定(Recover all bindings)

     4、恢复所有消费者(Recover all consumers)

要启用自动连接恢复,请将ConnectionFactory.AutomaticRecoveryEnabled设置为true:

  1. ConnectionFactory factory = new ConnectionFactory();
  2. factory.AutomaticRecoveryEnabled = true;
  3. // connection that will recover automatically
  4. IConnection conn = factory.CreateConnection();
如果恢复由于异常(例如RabbitMQ节点仍然不可达)失败,将在固定的时间间隔(默认为5秒)后重试。 间隔可以配置:
  1. ConnectionFactory factory = new ConnectionFactory();
  2. // attempt recovery every 10 seconds
  3. factory.NetworkRecoveryInterval = TimeSpan.FromSeconds(10);

拓扑恢复

拓扑恢复涉及恢复queues, exchanges, bindings, and consumers。 默认情况下启用它,但可以禁用:

  1. ConnectionFactory factory = new ConnectionFactory();
  2. Connection conn = factory.CreateConnection();
  3. factory.AutomaticRecoveryEnabled = true;
  4. factory.TopologyRecoveryEnabled = false;
手动确认和自动恢复
当使用手动确认时,可能与RabbitMQ节点的网络连接在消息传递和确认之间失败。 在连接恢复后,RabbitMQ将重置所有通道上的交付标签。 这意味着使用旧的传递标记的basic.ack,basic.nack和basic.reject将导致通道异常。 为了避免这种情况,RabbitMQ .NET客户端跟踪和更新传递标记,使它们在恢复之间单调增长。 IModel.BasicAck,IModel.BasicNack和IModel.BasicReject然后将调整后的交付标签转换为RabbitMQ使用的标签。 不会发送过期交货标签的确认。 使用手动确认和自动恢复的应用程序必须能够处理重新递送。

使用AMQP 0-9-1的常见方法(Common ways of working with AMQP 0-9-1

当使用RabbitMQ构建分布式系统时,会有一些不同的消息模式反复出现。在本节中,我们将介绍一些最常见的编码模式和交互风格:


    点对点消息:远程过程调用(RPC)和指向特定接收器的异步消息。

    事件广播:一对多交互;隐含地指向一组感兴趣的接收者的消息的传输,以及零个或多个可能的响应的收集。

    责任转移:选择网络中的哪个部分负责任何给定的消息。

    消息传输:至少一次和最多一次消息传递。

    在与外部资源交互时保持原子性和幂等性。


有限库支持也可用于处理这些模式,在RabbitMQ.Client.MessagePatterns命名空间:


    订阅提供了从服务器接收消息的高级接口。

    SimpleRpcServer构建在Subscription上以实现RPC或单向服务。

    SimpleRpcClient构建在Subscription上,与远程服务交互。


将来的RabbitMQ .NET客户端库版本将包括改进对最常见的消息传递模式及其变体的高级支持。


点对点消息(Point-to-point Messaging


当消息的发布者具有特定的接收应用时,例如,当通过AMQP服务器使得RPC样式的服务可用时,或者当工作流链中的应用接收到消息时,发生点对点消息传递模式工作项,并将转换后的工作项发送给其后继者。

同步,客户机 - 服务器远程过程调用(RPC)

为了执行请求/响应RPC,


    一些解决服务的手段必须可用

    一些接收答复的方法必须可用

    将请求消息与回复消息相关联的一些装置必须可用


寻址服务(Addressing the service

由于AMQP消息是使用一对交换名称和路由密钥发布的,因此这足以用于寻址服务。使用简单的交换名/路由 - 密钥组合允许多种不同的方式来实现服务,同时向客户端呈现相同的接口。例如,服务可以被实现为从队列消耗的单个进程和在内部的负载均衡,或者其可以是从单个队列消耗的多个进程,被递送请求循环式,从而在没有特殊编码的情况下进行负载均衡服务逻辑。消息也可以寻址到服务请求队列


    直接,使用AMQP默认交换(“”);要么

    间接地通过使用服务特定交换,其使得路由密钥免费用于诸如方法选择或附加服务特定寻址信息的目的;要么

    间接地,通过使用由多个服务共享的交换,其中服务名称在路由密钥中编码。


使用默认交换之外的交换允许其他应用程序接收每个请求消息的副本,这对于监视,审计,日志记录和调试是有用的。

确保服务实例正在侦听


AMQP 0-9-1发布操作(IModel.BasicPublish)提供了交付标志“强制性”,可用于确保客户端发送请求时的服务可用性。如果不能将请求路由到队列,则设置“mandatory”标志会导致返回请求。返回的消息显示为basic.return命令,通过IModel上用于发布消息的IModel.BasicReturn事件使其可见。


由于已发布的消息通过basic.return方法返回到客户端,而basic.return是异步否定确认事件,因此不能将特定消息的basic.return作为传递的确认:使用传递标志只提供了提高杆的方法,而不是完全消除故障。


另外,消息被标记为“强制性”并且成功地入队在一个或多个队列上的事实不能保证其最终接收:最平常地,队列可以在消息被处理之前被删除,但是其他情况,例如使用noAck标志的消息消费者,也可以使得“强制”提供的保证有条件。


或者,您可以使用发布商确认。通过调用IModel.ConfirmSelect将通道设置为确认模式会导致代理在通过传递到就绪消费者或持久存储到磁盘来处理每个消息后发送Basic.Ack。一旦通过IModel.BasicAcks事件处理程序确认成功处理的消息,代理就承担了该消息的责任,客户端可以考虑处理消息。注意,代理还可以通过发送回Basic.Nack来否定确认消息。在这种情况下,如果通过IModel.BasicNacks事件处理程序拒绝消息,客户端应该假定消息丢失或以其他方式无法投递。此外,请注意,不可路由的消息 - 发布为不存在队列的强制性消息 - 都是Basic.Return和Basic.Ack'ed。


接收回复(Receiving Replies

AMQP 0-9-1内容头(IBasicProperties)包含一个称为ReplyTo的字段,可用于告知服务在何处发布对接收到的RPC请求的答复。在当前的RabbitMQ客户端库中,ReplyTo头中的字符串使用最广泛的格式是一个简单的队列名称,尽管传递通过应用程序特定规则加入的交换名称和路由键也是一个选项。服务实例将其答复发布到指定的目的地,并且请求客户端应该安排接收如此寻址的消息,使用在适当绑定的队列上的BasicGet或BasicConsume。

将接收到的应答与发送的请求相关联

IBasicProperties包含一个名为CorrelationId的字段,在AMQP 0-9-1中是一个非结构化字符串,可用于将请求匹配到回复。应答消息应具有与附加到请求消息的相同的相关标识。


异步,单向消息传递(Asynchronous, one-way messaging

在某些情况下,简单的请求 - 回复交互模式不适合您的应用程序。 在这些情况下,感兴趣的交互模式可以从异步,单向,点对点消息构造。 如果应用程序要响应同步,RPC样式请求和异步单向请求,它应该使用ReplyTo的值来决定请求它的交互样式:如果ReplyTo存在并且非空, 请求可以假定是一个RPC样式的调用; 否则,应假定它是单向消息。 CorrelationId字段可以用于将多个相关消息分组在一起,就像对于RPC样式的情况一样,但是更通常地将任意数量的消息绑定在一起。


点对点的确认模式(Acknowledgment modes for point-to-point)

当从服务器接收消息时,AMQP可以以两种模式之一操作:自动确认模式(当BasicGet,BasicConsume或Subscription构造函数上设置noAck标志时)或手动确认模式。选择正确的确认模式对于您的应用程序很重要:


    自动确认模式意味着当服务器在网络上传输消息时,服务器将内部将消息标记为已成功传递。以自动确认模式传送的消息通常不会重新传送到任何其他接收器。

    手动确认模式意味着在将消息标记为已成功传送之前,服务器将等待接收的肯定确认。如果在服务器接收到确认之前关闭了交付的手动确认模式下的通道(IModel),则将重新排队。


一般来说,

    如果服务处于手动确认模式,则它不应该确认请求消息,直到它回复它;请参阅下面有关与外部资源交互的部分。

    客户端可以使用自动确认模式,这取决于请求消息的重传的结果。


库支持点对点消息传递(Library support for point-to-point messaging)

RabbitMQ .NET客户端库包括涉及点对点消息传递的常见任务的基本支持。

SimpleRpcServer

类RabbitMQ.Client.MessagePatterns.SimpleRpcServer实现同步RPC样式的请求处理以及异步消息处理。用户应该继承SimpleRpcServer,覆盖一个或多个以“Handle”开头的方法。 SimpleRpcServer实例具有请求调度循环MainLoop,它将请求解释为RPC样式的请求,如果请求的IBasicProperties的ReplyTo字段为非空且非空,则需要回复。具有缺少或空的ReplyTo字段的请求被视为单向。当处理了RPC样式的请求时,将答复发送到ReplyTo地址。答复地址首先与描述上面给出的类似URI的语法的正则表达式相匹配;如果匹配,则使用类似URI的语法的组件作为回复地址,如果不匹配,则将整个字符串用作简单队列名称,并将回复发送到默认交换(“”)一个等于ReplyTo字符串的路由键。

SimpleRpcClient

类RabbitMQ.Client.MessagePatterns.SimpleRpcClient实现与SimpleRpcServers或类似的交互的代码。 RPC风格的交互是用Call方法执行的。 (私人)订阅设置为从服务接收回复,并且ReplyTo字段设置为指向订阅。请求的CorrelationId字段被初始化为新的GUID。异步/单向交互被简单地传递到IModel.BasicPublish而不修改:它是由调用者在异步情况下设置CorrelationId。该类目前不支持在已发布的请求消息上设置“mandatory”标志,也不支持处理由于设置该标志而可能产生的任何BasicReturn事件。从内部订阅检索答复的代码当前无法处理多个同时未解决的RPC请求,因为它要求答复以与发送请求相同的顺序到达。在解除此限制之前,不要尝试管理通过SimpleRpcClient的单个实例发送的请求。另请参见可覆盖的受保护方法SimpleRpcClient.RetrieveReply。使用SimpleRpcClient的基本模式如下:

  1. using (IConnection conn = new ConnectionFactory()
  2. .CreateConnection(args[0])) {
  3. using (IModel ch = conn.CreateModel()) {
  4. SimpleRpcClient client = new SimpleRpcClient(ch, /* ... */);
  5. // in the line above, the "..." indicates the parameters
  6. // used to specify the address to use to route messages
  7. // to the service.
  8. // The next three lines are optional:
  9. client.TimeoutMilliseconds = 5000; // defaults to infinity
  10. client.TimedOut += new EventHandler(TimedOutHandler);
  11. client.Disconnected += new EventHandler(DisconnectedHandler);
  12. byte[] replyMessageBytes = client.Call(requestMessageBytes);
  13. // other useful overloads of Call() and Cast() are
  14. // available. See the code documentation of SimpleRpcClient
  15. // for full details.
  16. }
  17. }
请注意,单个SimpleRpcClient实例可以执行许多(顺序)Call()和Cast()请求! 建议单个SimpleRpcClient重复用于多个服务请求,只要请求是严格顺序的。


事件广播(Event Broadcasting)

当应用程序希望在不知道每个感兴趣方的地址的情况下向应用程序池指示状态改变或其他通知时,发生事件广播模式。 对某个事件子集感兴趣的应用程序使用交换和队列绑定来配置哪些事件被路由到其自己的专用队列。


通常,事件将通过主题交换广播,但是直接交换虽然不太灵活,但是有时对于其有限模式匹配能力足够的应用可以执行得更好。


发布事件(Publishing events

要发布事件,首先确保交换存在,然后确定适当的路由密钥。 例如,对于股票,一个键如“stock.ibm.nyse”可能是合适的; 对于其他应用程序,其他主题层次结构将自然出现。 主题交换常用。 然后发布消息。 例如:

  1. using (IConnection conn = new ConnectionFactory()
  2. .CreateConnection(args[0])) {
  3. using (IModel ch = conn.CreateModel()) {
  4. IBasicProperties props = ch.CreateBasicProperties();
  5. FillInHeaders(props); // or similar
  6. byte[] body = ComputeBody(props); // or similar
  7. ch.BasicPublish("exchangeName",
  8. "chosen.routing.key",
  9. props,
  10. body);
  11. }
  12. }
请参阅RabbitMQ.Client.IModel类中的BasicPublish的各种重载的文档。

订阅(Subscription)
RabbitMQ.Client.MessagePatterns.Subscription类实现了大多数接收消息(包括,特别是广播事件)的样板,包括消费者声明和管理,但不包括队列和交换声明和队列绑定。例如,
  1. // "IModel ch" in scope.
  2. Subscription sub = new Subscription(ch, "STOCK.IBM.#");
  3. foreach (BasicDeliverEventArgs e in sub) {
  4. // handle the message contained in e ...
  5. // ... and finally acknowledge it
  6. sub.Ack(e);
  7. }
将使用IModel.BasicConsume在队列上启动一个消费者。它假定队列和任何绑定以前已经声明。应该为每个接收的事件调用Subscription.Ack(),无论是否使用自动确认模式,因为Subscription内部知道是否需要确认的实际网络消息,并以有效的方式为您处理只要在你的代码中总是调用Ack()。有关完整的详细信息,请参阅Subscription类的代码文档。

使用自定义消费者检索事件(Retrieving events with a custom consumer)
有时,使用Subscription的高级方法是足够的。 然而,其他时候,需要使用定制消费者。 这种检索事件的方法是将队列绑定到与适当的路由 - 密钥模式规范相关的交换。 例如,假设我们的应用程序想要在队列“MyApplicationQueue”上检索有关IBM的所有价格:         

  1. // "IModel ch" in scope.
  2. ch.ExchangeDeclare("prices", "topic");
  3. ch.QueueDeclare("MyApplicationQueue", false, true, true, null);
  4. ch.QueueBind("MyApplicationQueue", "prices",
  5. "STOCK.IBM.#", false, null);
然后使用BasicGet或BasicConsume从“MyApplicationQueue”消耗消息。 一个更完整的例子在ApiOverview章节。


事件广播的确认模式(Acknowledgment modes for event broadcasting

与用于点对点消息传递的相同的自动确认/手动确认决定可用于广播事件的消费者,但是交互的模式引入不同的权衡:


    对于高容量消息传递,其中偶尔可接受的是不接收一个感兴趣的消息,自动确认模式是有意义的

    对于其中满足我们的订阅的每个消息需要被递送的情况,手动确认是适当的


有关详细信息,请参阅下面的可靠邮件传输部分。还要注意,只要为每个接收的消息调用Subscription.Ack(),类Subscription就会负责确认和各种确认模式。


可靠的消息传输(Reliable message transfer)

消息可以在具有不同服务质量(QoS)水平的端点之间传输。一般来说,不能完全排除故障,但重要的是要了解各种交付故障模式,以了解从故障中恢复的种类,以及可能恢复的情况。重申:不可能完全排除故障。可以做的最好是缩小可能发生故障的条件,并且当检测到故障时通知系统操作员。

至少一次递送


该QoS水平确保消息被传递到其最终目的地至少一次。也就是说,接收器可以接收消息的多个副本。如果对于给定消息,副作用仅发生一次是重要的,则应该使用至多一次递送。


要实施至少一次投放(At-least-once delivery)

    像往常一样发布消息,在其上具有一些相关标识符和回复地址,使得接收方可以确认对发送方的接收。当接收到消息时,将确认消息发送回发送者。如果消息是RPC请求,则RPC应答消息隐式地是对请求的接收的确认。

    或者,不是手动实现往返逻辑,而是客户端可以使用发布者确认。通过在通道上启用确认模式,客户端请求代理确认或否定确认从该点开始在该通道上发送的所有消息。请参阅“责任转移”中有关如何使用确认的说明。


决定邮件重发策略可能很困难。一些简单的重新发送策略是:


    如果您的连接丢失或在您收到收据确认之前发生其他崩溃,请重新发送

    如果您在几秒钟内没有收到确认,则超时并重新发送。请确保每次重新发送的超时时间加倍,以帮助避免与重试相关的拒绝服务和网络拥塞。


最多一次传送(At-most-once delivery)

对于最多一次传递,只需发布消息,一次,照常。不需要相关标识符。在使用应用程序中接收消息,注意交货时的Redelivered标志。 Redelivered标志只有在服务器认为它提供第一次消息消息时才会清除。如果之前已进行任何交货尝试,则重新送达标志将被设置。 Redelivered标志是一个非常有限的信息,只给出最多一次的语义。


用多节点RabbitMQ集群编码(Coding with multi-node RabbitMQ clusters

在需要连续服务的情况下,可以通过一些仔细的编程和用于故障转移的热备份集群的可用性来对抗服务器故障的可能性。

失败时的主要关注点是


    公布/承认的工作单位的原子性,以及备份服务器上已配置资源的可用性


消息生产者应注意使用事务,以便从服务器接收一组消息的肯定确认,并且应该保留他们为了执行它们的工作需要可用的交换,队列和绑定的记录,因此在故障切换时,可以在重放最近的要恢复的事务之前声明适当的资源。


消息消费者应该意识到在故障转移时丢失或重复消息的可能性:发布者可以决定重新发送其结果有疑问的事务,或者发布者认为完成的事务可能由于集群节点的故障而完全消失。

与外部资源交互

服务的常见模式是


    经由队列接收服务请求

    更新一些外部资源,如文件或数据库

    通过RabbitMQ进行回复,或至少向服务器确认触发操作的消息已完成


至少一次模式的元素通常与外部资源模式一起出现 - 具体地,上面关于可靠消息传递的部分中讨论的副作用通常对外部资源产生影响。


在交付必须被处理不超过一次并且与外部资源结合使用的情况下,重要的是编写能够在每个步骤的代码以确定该步骤是否已经在完成整个交易的一些先前尝试中采取,并且如果它具有,则能够在该尝试中省略它并继续下一步骤。例如:


    如果工作触发请求丢失,另一个副本将(最终)从最终请求者到达。

    如果已经执行了工作,例如更新了数据库表,则在先前接收到有问题的工作项时,服务需要保持对于原子工作本身的原子的外部工作的完成的记录:例如,在相同的数据库事务中,可以更新尊敬请求的一些日志,或者可以更新被修改的行以包括引起修改的请求的ID,以及修改该行的先前请求ID题。


这使得重要的是能够压缩请求ID,以便它们不在执行的工作的日志中占用无界空间,并且使得我们不需要与最终请求者引入完全分布式垃圾收集协议。这样做的一种方式是选择使用严格增加的请求ID,使得可以使用“高水位线”。一旦知道已经执行了工作,并且已经产生了答复(如果存在答复),则可以根据需要将答复发送回请求者。请求者知道它期望的回复,并且可以丢弃不想要的重复。只要相同请求的重复总是收到相同的答复消息,则复制者不必小心发送太多的复制副本。一旦已经将答复发送到服务器,则可以确认请求消息为已接收并且与服务器服务器一起处理。在没有对请求的答复的情况下,确认仍然有用以确保请求不丢失。






posted @ 2017-01-18 17:48  zero.huang  阅读(20885)  评论(1编辑  收藏  举报