0

I have a scenario in which I am calling RegisterMessageHandler of SubscriptionClient class of Azure Service Bus library. Basically I am using trigger based approach while receiving the messages from Service Bus in one of my services in Service Fabric Environment as a stateless service. So I am not closing the subscriptionClient object immediately, rather I am keeping it open for the lifetime of the Service so that it keeps on receiving the message from azure service bus topics.

And when the service needs to shut down(due to some reasons), I want to handle the cancellation token being passed into the service of Service Fabric.

My question is how can I handle the cancellation token in the RegisterMessageHandler method which gets called whenever a new message is received? Also I want to handle the closing of the Subscription client "Gracefully", i.e I want that if a message is already being processed, then I want that message to get processed completely and then I want to close the connection. Below is the code I am using.

Currently We are following the below approach: 1. Locking the process of the message using semaphore lock and releasing the lock in finally block. 2. Calling the cancellationToken.Register method to handle cancellation token whenever cancellation is done. Releasing the lock in the Register Method.

public class AzureServiceBusReceiver
{
  private SubscriptionClient subscriptionClient;
  private static Semaphore semaphoreLock;

public AzureServiceBusReceiver(ServiceBusReceiverSettings settings)
{
    semaphoreLock = new Semaphore(1, 1);
    subscriptionClient = new SubscriptionClient(
        settings.ConnectionString, settings.TopicName, settings.SubscriptionName, ReceiveMode.PeekLock);
}

public void Receive(
    CancellationToken cancellationToken)
{
    var options = new MessageHandlerOptions(e =>
    {

        return Task.CompletedTask;
    })
    {
        AutoComplete = false,

    };

    subscriptionClient.RegisterMessageHandler(
        async (message, token) =>
        {
            semaphoreLock.WaitOne();
            if (subscriptionClient.IsClosedOrClosing)
                return;
            CancellationToken combinedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, token).Token;
            try
            {
                // message processing logic
            }
            catch (Exception ex)
            {
                await subscriptionClient.DeadLetterAsync(message.SystemProperties.LockToken);
            }
            finally
            {
                semaphoreLock.Release();
            }
        }, options);


    cancellationToken.Register(() =>
    {
        semaphoreLock.WaitOne();
        if (!subscriptionClient.IsClosedOrClosing)
            subscriptionClient.CloseAsync().GetAwaiter().GetResult();
        semaphoreLock.Release();
        return;
    });
} 
}
Anand
  • 47
  • 2
  • 12
  • There's an issue with the .NET SDK to add support for `CancellationToken`. You can upvote and add your comments [there](https://github.com/Azure/azure-sdk-for-net/issues/6653). – Sean Feldman Jan 07 '20 at 19:11

1 Answers1

0

Implement the message client as ICommunicationListener, so when the service is closed, you can block the call until message processing is complete. Don't use a static Semaphore, so you can safely reuse the code within your projects.

Here is an example of how you can do this.

And here's the Nuget package created by that code.

And feel free to contribute!

LoekD
  • 11,402
  • 17
  • 27