[英]Rabbit MQ handle cancellation token
我正在關注本文( http://www.jarloo.com/listening-to-rabbitmq-events/ ),以便在控制台應用程序上使用消息,目前我擔心當用戶按下CTRL + C並退出時會發生什么情況應用程序。
至少,我希望它在退出程序之前完成對當前消息和ack的處理。 我對RabbitMQ的新手感到困惑,我該如何實現該代碼。
我了解該channel.BasicConsume(queueName,true,消費者); 正在阻塞線程。
任何幫助將不勝感激。
對於該問題,您嘗試解決的只有我能想到的就是將其視為事務。.僅當您收到消息時,對其進行完全處理並發送回Ack,才認為該事務已完成。
如果您首先處理該消息,並且有人在Ack之前終止了該應用程序,則它將再次排隊,而當我們重新啟動該應用程序時,它將再次得到處理。
與此相反,如果您先確認然后嘗試處理該消息,但有人終止了該應用程序,則您將丟失該消息。
因此,看起來好像在考慮整個過程,因為事務將使它正常工作,或者另一個選擇是照顧再次處理同一消息。
這是我設法實現的目標,但不確定是最好的方法還是有所改進。 我尚未使用cancellingToken,但不確定是否可以使用它。
在我的控制台上,我得到了預期的結果(請參見屏幕截圖)
public abstract class QueueConnection : IDisposable
{
internal IConnection _connection;
internal IModel _model;
internal IBasicProperties _properties;
internal RabbitMQSettings _settings;
internal protected object _modelLock = new Object();
public QueueConnection(IOptions<RabbitMQSettings> queueConfig)
{
_settings = queueConfig.Value;
}
internal bool CreateModel(string queueName)
{
if (string.IsNullOrEmpty(queueName))
{
throw new ArgumentException("The queue name has to be specified before.");
}
lock (_modelLock)
{
if (!IsConnected) Connect();
if (_model == null || _model.IsClosed)
{
_model = _connection.CreateModel();
// When AutoClose is true, the last channel to close will also cause the connection to close.
// If it is set to true before any channel is created, the connection will close then and there.
_connection.AutoClose = true;
// Configure the Quality of service for the model. Below is how what each setting means.
// BasicQos(0="Dont send me a new message untill I’ve finshed", 1= "Send me one message at a time", false ="Apply to this Model only")
_model.BasicQos(0, 50, false);
const bool durable = true, queueAutoDelete = false, exclusive = false;
_model.QueueDeclare(queueName, durable, exclusive, queueAutoDelete, null);
_properties = RabbitMQProperties.CreateDefaultProperties(_model);
}
}
return true;
}
public void Connect()
{
var connectionFactory = new ConnectionFactory
{
HostName = _settings.HostName,
UserName = _settings.UserName,
Password = _settings.Password,
};
if (_settings.Port.HasValue) connectionFactory.Port = _settings.Port.Value;
if (_settings.Heartbeat.HasValue) connectionFactory.RequestedHeartbeat = _settings.Heartbeat.Value;
if (!string.IsNullOrEmpty(_settings.VirtualHost)) connectionFactory.VirtualHost = _settings.VirtualHost;
_connection = connectionFactory.CreateConnection();
}
public bool IsConnected
{
get { return _connection != null && _connection.IsOpen; }
}
public object GetConnection()
{
return _connection;
}
public void Disconnect()
{
if (_connection != null) _connection.Dispose();
}
void IDisposable.Dispose()
{
Disconnect();
}
}
QueueConsumer類
public class QueueConsumer : QueueConnection, IQueueConsumer
{
private EventingBasicConsumer consumer;
public QueueConsumer(IOptions<RabbitMQSettings> queueConfig)
:base(queueConfig) {}
public void ReadFromQueue(Action<string, ulong> onDequeue, Action<Exception, ulong> onError)
{
ReadFromQueue(onDequeue, onError, _settings.QueueName);
}
public void ReadFromQueue(Action<string, ulong> onDequeue, Action<Exception, ulong> onError, string queueName)
{
CreateModel(queueName);
consumer = new EventingBasicConsumer(_model);
// Receive the messages
consumer.Received += (o, e) =>
{
try
{
var queueMessage = Encoding.UTF8.GetString(e.Body);
onDequeue.Invoke(queueMessage, e.DeliveryTag);
}
catch (Exception ex)
{
onError.Invoke(ex, e.DeliveryTag);
}
};
// if the consumer shutdown reconnects to rabbitmq and begin reading from the queue again.
consumer.Shutdown += (o, e) =>
{
CreateModel(queueName);
ReadFromQueue(onDequeue, onError, queueName);
};
_model.BasicConsume(queueName, false, consumer);
}
public void AcknowledgeMessage(ulong deliveryTag)
{
if (!IsConnected) Connect();
CreateModel(_settings.QueueName);
_model.BasicAck(deliveryTag, false);
}
public void StopListening()
{
_model.BasicCancel(consumer.ConsumerTag);
}
}
主班
static ManualResetEvent _quitEvent = new ManualResetEvent(false);
public static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
ConfigureServices(services);
var serviceProvider = services.BuildServiceProvider();
Console.WriteLine($"[{DateTime.UtcNow.ToString("dd/MM/yyyy HH:mm:ss")}] -> Worker role started");
var listener = serviceProvider.GetService<IMessageProcessor>();
Console.CancelKeyPress += (sender, eArgs) =>
{
listener.OnStop();
Console.WriteLine($"[{DateTime.UtcNow.ToString("dd/MM/yyyy HH:mm:ss")}] -> Worker role finished");
_quitEvent.Set();
eArgs.Cancel = true;
};
_quitEvent.WaitOne();
}
private static IConfigurationRoot GetConfiguration()
{
// Build appsetting.json configuration
var environment = Environment.GetEnvironmentVariable("Environment");
return new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true)
.AddEnvironmentVariables().Build();
}
private static void ConfigureServices(IServiceCollection services)
{
IConfigurationRoot configuration = GetConfiguration();
services.AddSingleton<IConfigurationRoot>(configuration);
// Support typed options
services.AddOptions();
services.Configure<RabbitMQSettings>(configuration.GetSection("RabbitMQConfig"));
services.AddSingleton<IQueueConsumer, QueueConsumer>();
services.AddScoped<IMessageProcessor, MessageProcessor>();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.