RabbitMQ C#驱动程序停止接收消息
您是否有任何关于如何确定何时发生订阅问题的指示,以便我可以重新连接 我的服务使用RabbitMQ.Client.MessagePatterns.Subscription作为订阅。过了一段时间,我的客户安静地停止接收消息。我怀疑网络问题,因为我认为我们的VPN连接不是最可靠的 我已经阅读了一段时间的文档,寻找一个密钥,以确定此订阅何时可能由于网络问题而中断,但运气不佳。我尝试检查连接和频道是否仍然打开,但它似乎总是报告它仍然打开 它确实处理的消息工作得很好,并且被确认回队列,所以我认为这不是“ack”的问题 我肯定我错过了一些简单的东西,但我还没有找到RabbitMQ C#驱动程序停止接收消息,c#,rabbitmq,C#,Rabbitmq,您是否有任何关于如何确定何时发生订阅问题的指示,以便我可以重新连接 我的服务使用RabbitMQ.Client.MessagePatterns.Subscription作为订阅。过了一段时间,我的客户安静地停止接收消息。我怀疑网络问题,因为我认为我们的VPN连接不是最可靠的 我已经阅读了一段时间的文档,寻找一个密钥,以确定此订阅何时可能由于网络问题而中断,但运气不佳。我尝试检查连接和频道是否仍然打开,但它似乎总是报告它仍然打开 它确实处理的消息工作得很好,并且被确认回队列,所以我认为这不是“ac
public void Run(string brokerUri, Action<byte[]> handler)
{
log.Debug("Connecting to broker: {0}".Fill(brokerUri));
ConnectionFactory factory = new ConnectionFactory { Uri = brokerUri };
using (IConnection connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
channel.QueueDeclare(queueName, true, false, false, null);
using (Subscription subscription = new Subscription(channel, queueName, false))
{
while (!Cancelled)
{
BasicDeliverEventArgs args;
if (!channel.IsOpen)
{
log.Error("The channel is no longer open, but we are still trying to process messages.");
throw new InvalidOperationException("Channel is closed.");
}
else if (!connection.IsOpen)
{
log.Error("The connection is no longer open, but we are still trying to process message.");
throw new InvalidOperationException("Connection is closed.");
}
bool gotMessage = subscription.Next(250, out args);
if (gotMessage)
{
log.Debug("Received message");
try
{
handler(args.Body);
}
catch (Exception e)
{
log.Debug("Exception caught while processing message. Will be bubbled up.", e);
throw;
}
log.Debug("Acknowledging message completion");
subscription.Ack(args);
}
}
}
}
}
}
public void运行(字符串代理,操作处理程序)
{
Debug(“连接到代理:{0}.Fill(brokerUri));
ConnectionFactory=newconnectionfactory{Uri=brokerUri};
使用(IConnection connection=factory.CreateConnection())
{
使用(IModel channel=connection.CreateModel())
{
QueueDeclare(queueName,true,false,false,null);
使用(订阅=新订阅(频道、队列名称、false))
{
而(!取消)
{
基本交付事件args args;
如果(!channel.IsOpen)
{
Error(“通道不再打开,但我们仍在尝试处理消息。”);
抛出新的InvalidOperationException(“通道已关闭”);
}
如果(!connection.IsOpen)
{
错误(“连接不再打开,但我们仍在尝试处理消息。”);
抛出新的InvalidOperationException(“连接已关闭”);
}
bool gotMessage=subscription.Next(250,out参数);
if(gotMessage)
{
log.Debug(“收到的消息”);
尝试
{
处理程序(args.Body);
}
捕获(例外e)
{
Debug(“处理消息时捕获的异常。将冒泡。”,e);
投掷;
}
调试(“确认消息完成”);
subscription.Ack(args);
}
}
}
}
}
}
更新:
我通过在虚拟机中运行服务器模拟了网络故障,当我中断连接足够长的时间时,我确实收到了一个异常(RabbitMQ.Client.Exceptions.operationTerruptedException:AMQP操作被中断),因此可能不是网络问题。现在我不知道它会是什么,但运行几个小时后就失败了。编辑:由于我正在对此进行投票,我应该指出,.NET RabbitMQ客户端现在内置了以下功能: 理想情况下,您应该能够使用它,并避免手动实现重新连接逻辑
我最近不得不实现几乎相同的事情。据我所知,RabbitMQ上的大多数可用信息都假定您的网络非常可靠,或者您在与发送或接收消息的任何客户端相同的机器上运行RabbitMQ代理,从而允许Rabbit处理任何连接问题 设置Rabbit客户端以使其对断开的连接具有健壮性其实并不难,但是您需要处理一些特性 您需要做的第一件事是打开心跳:
ConnectionFactory factory = new ConnectionFactory()
{
Uri = brokerUri,
RequestedHeartbeat = 30,
};
将“RequestedHeartbeat”设置为30将使客户端每30秒检查一次连接是否仍处于活动状态。如果不启用此选项,消息订阅者将愉快地坐在那里等待另一条消息传入,而不知道其连接是否已坏
打开heartbeat还可以让服务器检查连接是否仍在运行,这一点非常重要。如果在订阅者拾取消息后但在确认之前,连接出现故障,则服务器只会假定客户端需要很长时间,消息会在断开的连接上“卡住”,直到关闭为止。当heartbeat打开时,服务器将识别连接何时发生故障并关闭连接,将消息放回队列中,以便其他订阅者可以处理它。没有心跳,我不得不手动进入并关闭兔子管理UI中的连接,以便将卡住的消息传递给订阅者
其次,您将需要处理操作中断异常
。正如您所注意到的,这通常是Rabbit客户端在注意到连接被中断时抛出的异常。如果在连接中断时调用了IModel.QueueDeclare()
,则会出现异常。通过处理订阅、频道和连接并创建新的来处理此异常
最后,您必须处理您的消费者在尝试使用来自封闭连接的消息时所做的事情。不幸的是,在Rabbit客户机中,使用队列中消息的每种不同方式的反应似乎都不同QueueingBasicConsumer
在关闭的连接上调用QueueingBasicConsumer.Queue.Dequeue
时抛出EndOfStreamException
EventingBasicConsumer
什么都不做,因为它只是在等待消息。从试用中我可以看出,Subscription
类
public bool Cancelled { get; set; }
IConnection _connection = null;
IModel _channel = null;
Subscription _subscription = null;
public void Run(string brokerUri, string queueName, Action<byte[]> handler)
{
ConnectionFactory factory = new ConnectionFactory()
{
Uri = brokerUri,
RequestedHeartbeat = 30,
};
while (!Cancelled)
{
try
{
if(_subscription == null)
{
try
{
_connection = factory.CreateConnection();
}
catch(BrokerUnreachableException)
{
//You probably want to log the error and cancel after N tries,
//otherwise start the loop over to try to connect again after a second or so.
continue;
}
_channel = _connection.CreateModel();
_channel.QueueDeclare(queueName, true, false, false, null);
_subscription = new Subscription(_channel, queueName, false);
}
BasicDeliverEventArgs args;
bool gotMessage = _subscription.Next(250, out args);
if (gotMessage)
{
if(args == null)
{
//This means the connection is closed.
DisposeAllConnectionObjects();
continue;
}
handler(args.Body);
_subscription.Ack(args);
}
}
catch(OperationInterruptedException ex)
{
DisposeAllConnectionObjects();
}
}
DisposeAllConnectionObjects();
}
private void DisposeAllConnectionObjects()
{
if(_subscription != null)
{
//IDisposable is implemented explicitly for some reason.
((IDisposable)_subscription).Dispose();
_subscription = null;
}
if(_channel != null)
{
_channel.Dispose();
_channel = null;
}
if(_connection != null)
{
try
{
_connection.Dispose();
}
catch(EndOfStreamException)
{
}
_connection = null;
}
}