Return 来自基于调用事件处理程序的函数的值
Return value from a function based on which event handler is called
我正在使用 RabbitMQ(.Net 客户端库)将 XML 消息从 WebAPI 发布到队列。如果已发布的消息成功保存到磁盘,我需要发送状态码 200,否则应发送状态码 400 returned。 RabbitMQ 客户端库提供了两个单独的事件来发送 ACK 或 NACK 事件,指示消息是否已保存。所以我的发布函数需要等待两个事件句柄中的任何一个在我 return http 响应之前被调用。
我怎样才能做到这一点?
Api 控制器动作
[HttpPost]
public HttpResponseMessage SendSomething()
{
...
bool success = _publisher.Publish(bytes);
if(success) // Send status 200
else // Send status 400
...
}
消息发布代码
public bool Publish(byte[] data)
{
..
channel.BasicAcks += OnAck;
channel.BasicNacks += OnNack;
channel.BasicPublish("", "test", null, data);
..
// Depending on if OnAck or OnNack is called I need to return true or false
return ??
}
private void OnNack(IModel model, BasicNackEventArgs args)
{
...
}
private void OnAck(IModel model, BasicAckEventArgs args)
{
...
}
如果我没看错,您需要将基于回调的异步例程转换为同步例程。天真的答案是让线程休眠并等待某些事情发生:
public bool Publish(byte[] data)
{
//..
bool? response = null;
channel.BasicAcks += (model, args) => response = true;
channel.BasicNacks += (model, args) => response = false;
channel.BasicPublish("", "test", null, data);
while (response == null)
Thread.Sleep(300);
return response.Value;
}
然而,这意味着您的响应时间将始终是 300 毫秒的倍数,并且您会遇到难以实现超时逻辑的问题。更好的答案可能是使用 ManualResetEvent
。这允许您的回调在收到响应后立即通知阻塞的线程,"setting" 事件:
public bool Publish(byte[] data)
{
//..
bool successful = false;
var responseReceivedEvent = new ManualResetEvent(false);
channel.BasicAcks += (model, args) =>
{
successful = true;
responseReceivedEvent.Set();
};
channel.BasicNacks += (model, args) =>
{
successful = false;
responseReceivedEvent.Set();
};
channel.BasicPublish("", "test", null, data);
responseReceivedEvent.WaitOne();
return successful;
}
在任何一种情况下,您都可以(或应该)实现某种 timeout/retry 逻辑,除非 RabbitMQ 为您执行此操作。您可以在最后一个示例中使用 ManualResetEvent.WaitOne()
.
的 WaitOne(int)
或 WaitOne(TimeSpan)
重载来执行此操作
我正在使用 RabbitMQ(.Net 客户端库)将 XML 消息从 WebAPI 发布到队列。如果已发布的消息成功保存到磁盘,我需要发送状态码 200,否则应发送状态码 400 returned。 RabbitMQ 客户端库提供了两个单独的事件来发送 ACK 或 NACK 事件,指示消息是否已保存。所以我的发布函数需要等待两个事件句柄中的任何一个在我 return http 响应之前被调用。
我怎样才能做到这一点?
Api 控制器动作
[HttpPost]
public HttpResponseMessage SendSomething()
{
...
bool success = _publisher.Publish(bytes);
if(success) // Send status 200
else // Send status 400
...
}
消息发布代码
public bool Publish(byte[] data)
{
..
channel.BasicAcks += OnAck;
channel.BasicNacks += OnNack;
channel.BasicPublish("", "test", null, data);
..
// Depending on if OnAck or OnNack is called I need to return true or false
return ??
}
private void OnNack(IModel model, BasicNackEventArgs args)
{
...
}
private void OnAck(IModel model, BasicAckEventArgs args)
{
...
}
如果我没看错,您需要将基于回调的异步例程转换为同步例程。天真的答案是让线程休眠并等待某些事情发生:
public bool Publish(byte[] data)
{
//..
bool? response = null;
channel.BasicAcks += (model, args) => response = true;
channel.BasicNacks += (model, args) => response = false;
channel.BasicPublish("", "test", null, data);
while (response == null)
Thread.Sleep(300);
return response.Value;
}
然而,这意味着您的响应时间将始终是 300 毫秒的倍数,并且您会遇到难以实现超时逻辑的问题。更好的答案可能是使用 ManualResetEvent
。这允许您的回调在收到响应后立即通知阻塞的线程,"setting" 事件:
public bool Publish(byte[] data)
{
//..
bool successful = false;
var responseReceivedEvent = new ManualResetEvent(false);
channel.BasicAcks += (model, args) =>
{
successful = true;
responseReceivedEvent.Set();
};
channel.BasicNacks += (model, args) =>
{
successful = false;
responseReceivedEvent.Set();
};
channel.BasicPublish("", "test", null, data);
responseReceivedEvent.WaitOne();
return successful;
}
在任何一种情况下,您都可以(或应该)实现某种 timeout/retry 逻辑,除非 RabbitMQ 为您执行此操作。您可以在最后一个示例中使用 ManualResetEvent.WaitOne()
.
WaitOne(int)
或 WaitOne(TimeSpan)
重载来执行此操作