我在ASP.NET Core Web应用程序中有一个接收器方法:
public void ReceiveMessage()
{
using (var connection = CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "QueueName",durable: false,exclusive: false,autoDelete: false,arguments: null);
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
//Do something
channel.BasicAck(deliveryTag: ea.DeliveryTag,multiple: false);
};
channel.BasicConsume(queue: "QueueName",autoAck: false,consumer: consumer);
//BAD!!!
while (true)
{
}
}
}
您会注意到我正在使用while(true),它闻起来很不好。我基本上需要保持这种方法的生命,并想知道其他人是怎么做到的?
此方法应始终保持活动状态,并自动按1对1的顺序处理消息
答案 0 :(得分:0)
您可以创建一个托管服务并在其中使用您的消息。此托管服务始终处于活动状态,可以接收所有消息。
public class ProductTopicConsumerService : ConsumerBase, IHostedService
{
public ProductTopicConsumerService(ConnectionFactory connectionFactory)
: base(connectionFactory, ExchangeTypes.topic)
{
try
{
Consume<ProductCreatedIntegrationEvent>();
}
catch (Exception ex)
{
Console.WriteLine($"Error => {ex?.Message}");
}
}
protected override string Exchange => "ProductExchangeTopic5";
protected override string Queue => "Product.Updated5";
protected override string AppId => "ProductCreatedConsumer";
protected override string QueueAndExchangeRoutingKey => "Product.*";
public virtual Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public virtual Task StopAsync(CancellationToken cancellationToken)
{
Dispose();
return Task.CompletedTask;
}
}
和 ConsumerBase 可能是这样的:
public abstract class ConsumerBase : RabbitMqClientBase
{
public ConsumerBase(ConnectionFactory connectionFactory, ExchangeTypes exchangeType)
: base(connectionFactory, exchangeType,false)
{
}
protected void Consume<TMessage>()
{
var consumer = new AsyncEventingBasicConsumer(Channel);
consumer.Received += OnEventReceived<TMessage>;
Channel.BasicConsume(queue: RefinedQueueName, autoAck: false, consumer: consumer);
}
private Task OnEventReceived<TMessage>(object sender, BasicDeliverEventArgs @event)
{
try
{
var body = Encoding.UTF8.GetString(@event.Body.ToArray());
var message = JsonConvert.DeserializeObject<TMessage>(body);
ReceiveAction(message);
Channel.BasicAck(@event.DeliveryTag, false);
return Task.CompletedTask;
}
catch (Exception ex)
{
Console.WriteLine($"Error occurred in OnEventReceived. Message: {ex?.Message}");
Channel.BasicNack(@event.DeliveryTag, false, true);
throw;
}
finally
{
}
}
protected virtual void ReceiveAction<TMessage>(TMessage message)
{
}
}
最后,rabbitMQBase 可能是这样的:
public abstract class RabbitMqClientBase : IDisposable
{
protected const string VirtualHost = "MQ";
protected abstract string Exchange { get; }
protected abstract string Queue { get; }
protected abstract string AppId { get; }
protected abstract string QueueAndExchangeRoutingKey { get; }
protected IModel Channel { get; private set; }
private IConnection _connection;
private readonly ConnectionFactory _connectionFactory;
private readonly ExchangeTypes _exchangeType;
private readonly bool _isPublisher;
protected RabbitMqClientBase(ConnectionFactory connectionFactory, ExchangeTypes exchangeType, bool isPublisher)
{
_connectionFactory = connectionFactory;
this._exchangeType = exchangeType;
this._isPublisher = isPublisher;
ConnectToRabbitMq();
}
protected internal string RefinedExchangeName => $"{VirtualHost}.{Exchange}";
protected internal string RefinedQueueName => $"{VirtualHost}.{Queue}";
protected internal string RefinedRoutingKey => $"{VirtualHost}.{QueueAndExchangeRoutingKey}";
private void ConnectToRabbitMq()
{
if (_connection == null || _connection.IsOpen == false)
{
_connection = _connectionFactory.CreateConnection();
}
if (Channel == null || Channel.IsOpen == false)
{
Channel = _connection.CreateModel();
Channel.ExchangeDeclare(exchange: RefinedExchangeName, type: _exchangeType.ToString(), durable: true, autoDelete: false);
if (!_isPublisher)
{
Channel.QueueDeclare(queue: RefinedQueueName, durable: true, exclusive: false, autoDelete: false);
Channel.QueueBind(queue: RefinedQueueName, exchange: RefinedExchangeName, routingKey: RefinedRoutingKey);
}
}
}
public void Dispose()
{
Channel?.Close();
Channel?.Dispose();
Channel = null;
_connection?.Close();
_connection?.Dispose();
_connection = null;
}
}
这是 RabbitMQ 的 Publisher|Prodcuer 类:
public interface IRabbitMqProducer<in T>
{
PublishResult Publish(T @event);
}
public abstract class ProducerBase<T> : RabbitMqClientBase, IRabbitMqProducer<T>
{
protected ProducerBase(ConnectionFactory connectionFactory, ExchangeTypes exchangeType)
: base(connectionFactory, exchangeType, true) { }
public PublishResult Publish(T @event)
{
var result = new PublishResult();
try
{
var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(@event));
var properties = Channel.CreateBasicProperties();
properties.AppId = AppId;
properties.ContentType = "application/json";
properties.DeliveryMode = 2; //persist mode
properties.Timestamp = new AmqpTimestamp(DateTimeOffset.UtcNow.ToUnixTimeSeconds());
Channel.BasicPublish(exchange: RefinedExchangeName, routingKey: RefinedRoutingKey, basicProperties: properties, body: body);
result.SetSuccess();
}
catch (Exception ex)
{
result.SetError(ex);
}
return result;
}
}
发布您的发布商应该是这样的:
public class ProductTopicProducerService : ProducerBase<ProductCreatedIntegrationEvent>
{
public ProductTopicProducerService(ConnectionFactory connectionFactory)
: base(connectionFactory,ExchangeTypes.topic)
{
}
protected override string Exchange => "ProductExchangeTopic5";
protected override string Queue => "";
protected override string AppId => "ProductCreatedConsumer";
protected override string QueueAndExchangeRoutingKey => "Product.*";
}
对于发布新消息,请遵循以下代码:
var result = _producer.Publish(@event);
if (!result.IsSuccess)
{
Console.WriteLine($"Error => {result.Description}");
}
外包您可以简化您的消费者。但这是我为 rabbitMQBase 创建基类的经验,现在发布者和消费者都可以使用它。
答案 1 :(得分:-1)
看看OWIN软件包-在NuGet中可用:Microsoft.Owin.Hosting
使用它时,您将自托管Web服务,并且您的应用程序将以以下调用开头:
WebApp.Start(queryServiceUrl, Startup);
“启动”是执行所有初始化的一种方法。您的应用/服务保持运行状态,并且将接受对指定URL的查询。
答案 2 :(得分:-1)
在没有循环的情况下,调用channel.BasicConsume
后的瞬间,整个对象(连接/通道)将超出范围,并通过using
语句立即进行处理/销毁。因此,没有循环,您的消费者实际上就不会消耗任何东西。
为确保使用者操作,您需要有一个无限循环,并具有适当的逻辑以在关闭程序时退出。这是用于RabbitMQ的.NET库的不幸设计,但事实就是如此。
while (_isRunning & channel.IsOpen) {
Thread.Sleep(1);
// Other application logic here; e.g. periodically break out of the
// loop to prevent unacknowledged messages from accumulating in the system
// (if you don't, random effects will guarantee that they eventually build up)
}