-
Notifications
You must be signed in to change notification settings - Fork 20
update: migrate RabbitMQ operations to async methods +semver: minor #953
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
guibranco
merged 3 commits into
main
from
feature/596-upgrade-rabbitmq-client-library-to-version-700
Jul 4, 2026
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,143 +1,170 @@ | ||
| using System; | ||
| using System.Text; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using CrispyWaffle.Log; | ||
| using CrispyWaffle.RabbitMQ.Utils.Communications; | ||
| using RabbitMQ.Client; | ||
| using RabbitMQ.Client.Events; | ||
|
|
||
| namespace CrispyWaffle.RabbitMQ.Helpers; | ||
|
|
||
| /// <summary> | ||
| /// A class that facilitates receiving messages from RabbitMQ queues or exchanges. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The <see cref="MessageReceiver"/> class provides methods to receive messages from RabbitMQ queues or exchanges, | ||
| /// handling the message reception process and invoking events when messages are received. | ||
| /// </remarks> | ||
| public class MessageReceiver | ||
| { | ||
| /// <summary> | ||
| /// The RabbitMQ connection connector. | ||
| /// </summary> | ||
| private readonly RabbitMQConnector _connector; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MessageReceiver"/> class. | ||
| /// </summary> | ||
| /// <param name="connector">The <see cref="RabbitMQConnector"/> to be used for connecting to RabbitMQ.</param> | ||
| /// <exception cref="ArgumentNullException">Thrown if the <paramref name="connector"/> is null.</exception> | ||
| public MessageReceiver(RabbitMQConnector connector) => | ||
| _connector = connector ?? throw new ArgumentNullException(nameof(connector)); | ||
|
|
||
| /// <summary> | ||
| /// Delegate for handling received messages. | ||
| /// </summary> | ||
| /// <param name="sender">The sender of the message.</param> | ||
| /// <param name="e">The <see cref="MessageReceivedArgs"/> containing details of the received message.</param> | ||
| public delegate void MessageReceivedHandler(object sender, MessageReceivedArgs e); | ||
|
|
||
| /// <summary> | ||
| /// Event triggered when a message is received from the queue or exchange. | ||
| /// </summary> | ||
| public event MessageReceivedHandler MessageReceived; | ||
|
|
||
| /// <summary> | ||
| /// Starts receiving messages from a RabbitMQ queue. | ||
| /// </summary> | ||
| /// <typeparam name="T">The type of message to receive, which should implement <see cref="IQueuing"/>.</typeparam> | ||
| /// <param name="autoAck">If set to <c>true</c>, messages will be acknowledged automatically.</param> | ||
| /// <param name="cancellationToken">A <see cref="CancellationToken"/> to allow cancellation of the operation.</param> | ||
| /// <remarks> | ||
| /// This method will begin a background task to receive messages from the specified queue. | ||
| /// </remarks> | ||
| public void ReceiveFromQueue<T>(bool autoAck, CancellationToken cancellationToken) | ||
| where T : class, IQueuing, new() | ||
| { | ||
| var queueName = Extensions.GetQueueName<T>(); | ||
|
|
||
| Task.Run( | ||
| () => DoWork(string.Empty, queueName, autoAck, cancellationToken), | ||
| cancellationToken | ||
| ) | ||
| .ConfigureAwait(false); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Starts receiving messages from a RabbitMQ exchange. | ||
| /// </summary> | ||
| /// <typeparam name="T">The type of message to receive, which should implement <see cref="IQueuing"/>.</typeparam> | ||
| /// <param name="autoAck">If set to <c>true</c>, messages will be acknowledged automatically.</param> | ||
| /// <param name="cancellationToken">A <see cref="CancellationToken"/> to allow cancellation of the operation.</param> | ||
| /// <remarks> | ||
| /// This method will begin a background task to receive messages from the specified exchange. | ||
| /// </remarks> | ||
| public void ReceiveFromExchange<T>(bool autoAck, CancellationToken cancellationToken) | ||
| where T : class, IQueuing, new() | ||
| { | ||
| var exchangeName = Extensions.GetExchangeName<T>(); | ||
|
|
||
| Task.Run( | ||
| () => DoWork(exchangeName, string.Empty, autoAck, cancellationToken), | ||
| cancellationToken | ||
| ) | ||
| .ConfigureAwait(false); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Handles the work of receiving messages from a specified exchange or queue. | ||
| /// </summary> | ||
| /// <param name="exchange">The name of the exchange (optional).</param> | ||
| /// <param name="queue">The name of the queue (optional).</param> | ||
| /// <param name="autoAck">If set to <c>true</c>, messages will be acknowledged automatically.</param> | ||
| /// <param name="cancellationToken">A <see cref="CancellationToken"/> to allow cancellation of the operation.</param> | ||
| /// <remarks> | ||
| /// This method connects to RabbitMQ, binds the queue to the exchange (if specified), | ||
| /// and starts listening for messages. It invokes the <see cref="MessageReceived"/> event when a message is received. | ||
| /// </remarks> | ||
| private void DoWork( | ||
| string exchange, | ||
| string queue, | ||
| bool autoAck, | ||
| CancellationToken cancellationToken | ||
| ) | ||
| { | ||
| using (var connection = _connector.ConnectionFactory.CreateConnection()) | ||
| using (var channel = connection.CreateModel()) | ||
| { | ||
| var consumer = new EventingBasicConsumer(channel); | ||
|
|
||
| var queueName = string.IsNullOrWhiteSpace(queue) | ||
| ? channel.QueueDeclare().QueueName | ||
| : queue; | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(exchange)) | ||
| { | ||
| channel.QueueBind(queueName, exchange, string.Empty); | ||
| } | ||
|
|
||
| consumer.Received += (_, args) => | ||
| { | ||
| if (MessageReceived == null) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| LogConsumer.Trace( | ||
| $"Message received from exchange: {exchange} | queue: {queue} | queue name: {queueName}" | ||
| ); | ||
|
|
||
| var body = Encoding.UTF8.GetString(args.Body.ToArray()); | ||
|
|
||
| var eventArgs = new MessageReceivedArgs { QueueName = queueName, Body = body }; | ||
|
|
||
| MessageReceived.Invoke(this, eventArgs); | ||
| }; | ||
|
|
||
| channel.BasicConsume(queue: queueName, autoAck: autoAck, consumer: consumer); | ||
|
|
||
| cancellationToken.WaitHandle.WaitOne(); | ||
| } | ||
| } | ||
| } | ||
| using System; | ||
| using System.Text; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using CrispyWaffle.Log; | ||
| using CrispyWaffle.RabbitMQ.Utils.Communications; | ||
| using RabbitMQ.Client; | ||
| using RabbitMQ.Client.Events; | ||
|
|
||
| namespace CrispyWaffle.RabbitMQ.Helpers; | ||
|
|
||
| /// <summary> | ||
| /// A class that facilitates receiving messages from RabbitMQ queues or exchanges. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The <see cref="MessageReceiver"/> class provides methods to receive messages from RabbitMQ queues or exchanges, | ||
| /// handling the message reception process and invoking events when messages are received. | ||
| /// </remarks> | ||
| public class MessageReceiver | ||
| { | ||
| /// <summary> | ||
| /// The RabbitMQ connection connector. | ||
| /// </summary> | ||
| private readonly RabbitMQConnector _connector; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="MessageReceiver"/> class. | ||
| /// </summary> | ||
| /// <param name="connector">The <see cref="RabbitMQConnector"/> to be used for connecting to RabbitMQ.</param> | ||
| /// <exception cref="ArgumentNullException">Thrown if the <paramref name="connector"/> is null.</exception> | ||
| public MessageReceiver(RabbitMQConnector connector) => | ||
| _connector = connector ?? throw new ArgumentNullException(nameof(connector)); | ||
|
|
||
| /// <summary> | ||
| /// Delegate for handling received messages. | ||
| /// </summary> | ||
| /// <param name="sender">The sender of the message.</param> | ||
| /// <param name="e">The <see cref="MessageReceivedArgs"/> containing details of the received message.</param> | ||
| public delegate void MessageReceivedHandler(object sender, MessageReceivedArgs e); | ||
|
|
||
| /// <summary> | ||
| /// Event triggered when a message is received from the queue or exchange. | ||
| /// </summary> | ||
| public event MessageReceivedHandler MessageReceived; | ||
|
|
||
| /// <summary> | ||
| /// Starts receiving messages from a RabbitMQ queue. | ||
| /// </summary> | ||
| /// <typeparam name="T">The type of message to receive, which should implement <see cref="IQueuing"/>.</typeparam> | ||
| /// <param name="autoAck">If set to <c>true</c>, messages will be acknowledged automatically.</param> | ||
| /// <param name="cancellationToken">A <see cref="CancellationToken"/> to allow cancellation of the operation.</param> | ||
| /// <remarks> | ||
| /// This method will begin a background task to receive messages from the specified queue. | ||
| /// </remarks> | ||
| public void ReceiveFromQueue<T>(bool autoAck, CancellationToken cancellationToken) | ||
| where T : class, IQueuing, new() | ||
| { | ||
| var queueName = Extensions.GetQueueName<T>(); | ||
|
|
||
| Task.Run( | ||
| () => DoWorkAsync(string.Empty, queueName, autoAck, cancellationToken), | ||
| cancellationToken | ||
| ) | ||
| .ConfigureAwait(false); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Starts receiving messages from a RabbitMQ exchange. | ||
| /// </summary> | ||
| /// <typeparam name="T">The type of message to receive, which should implement <see cref="IQueuing"/>.</typeparam> | ||
| /// <param name="autoAck">If set to <c>true</c>, messages will be acknowledged automatically.</param> | ||
| /// <param name="cancellationToken">A <see cref="CancellationToken"/> to allow cancellation of the operation.</param> | ||
| /// <remarks> | ||
| /// This method will begin a background task to receive messages from the specified exchange. | ||
| /// </remarks> | ||
| public void ReceiveFromExchange<T>(bool autoAck, CancellationToken cancellationToken) | ||
| where T : class, IQueuing, new() | ||
| { | ||
| var exchangeName = Extensions.GetExchangeName<T>(); | ||
|
|
||
| Task.Run( | ||
| () => DoWorkAsync(exchangeName, string.Empty, autoAck, cancellationToken), | ||
| cancellationToken | ||
| ) | ||
| .ConfigureAwait(false); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Handles the work of receiving messages from a specified exchange or queue. | ||
| /// </summary> | ||
| /// <param name="exchange">The name of the exchange (optional).</param> | ||
| /// <param name="queue">The name of the queue (optional).</param> | ||
| /// <param name="autoAck">If set to <c>true</c>, messages will be acknowledged automatically.</param> | ||
| /// <param name="cancellationToken">A <see cref="CancellationToken"/> to allow cancellation of the operation.</param> | ||
| /// <remarks> | ||
| /// This method connects to RabbitMQ, binds the queue to the exchange (if specified), | ||
| /// and starts listening for messages. It invokes the <see cref="MessageReceived"/> event when a message is received. | ||
| /// </remarks> | ||
| private async Task DoWorkAsync( | ||
| string exchange, | ||
| string queue, | ||
| bool autoAck, | ||
| CancellationToken cancellationToken | ||
| ) | ||
| { | ||
| var connection = await _connector | ||
| .ConnectionFactory.CreateConnectionAsync(cancellationToken) | ||
| .ConfigureAwait(false); | ||
| await using var connectionDisposable = connection.ConfigureAwait(false); | ||
|
|
||
| var channel = await connection | ||
| .CreateChannelAsync(cancellationToken: cancellationToken) | ||
| .ConfigureAwait(false); | ||
| await using var channelDisposable = channel.ConfigureAwait(false); | ||
|
guibranco marked this conversation as resolved.
|
||
|
|
||
| var consumer = new AsyncEventingBasicConsumer(channel); | ||
|
|
||
| var queueName = string.IsNullOrWhiteSpace(queue) | ||
| ? ( | ||
| await channel.QueueDeclareAsync(cancellationToken: cancellationToken) | ||
| .ConfigureAwait(false) | ||
| ).QueueName | ||
| : queue; | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(exchange)) | ||
| { | ||
| await channel | ||
| .QueueBindAsync( | ||
| queueName, | ||
| exchange, | ||
| string.Empty, | ||
| cancellationToken: cancellationToken | ||
| ) | ||
| .ConfigureAwait(false); | ||
| } | ||
|
|
||
| consumer.ReceivedAsync += (_, args) => | ||
| { | ||
| if (MessageReceived == null) | ||
| { | ||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
| LogConsumer.Trace( | ||
| $"Message received from exchange: {exchange} | queue: {queue} | queue name: {queueName}" | ||
| ); | ||
|
|
||
| var body = Encoding.UTF8.GetString(args.Body.ToArray()); | ||
|
|
||
| var eventArgs = new MessageReceivedArgs { QueueName = queueName, Body = body }; | ||
|
|
||
| MessageReceived.Invoke(this, eventArgs); | ||
|
|
||
| return Task.CompletedTask; | ||
| }; | ||
|
|
||
| await channel | ||
| .BasicConsumeAsync(queueName, autoAck, consumer, cancellationToken) | ||
| .ConfigureAwait(false); | ||
|
|
||
| try | ||
| { | ||
| await Task.Delay(Timeout.Infinite, cancellationToken).ConfigureAwait(false); | ||
| } | ||
| catch (OperationCanceledException) | ||
| { | ||
| // Expected when cancellation is requested; allows graceful shutdown. | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.