如何确保在关闭MessageReciever期间完成MessageHandler委托?

时间:2018-06-20 08:19:58

标签: c# .net azure azureservicebus

我有一项服务,该服务连续使用result中的MessageReceiver来侦听ServiceBus中的订阅。当服务停止时,我想给所有操作完成一个机会,然后终止该进程。

这是我根据库提供的example使用的代码:

Microsoft.Azure.ServiceBus

在服务停止时,我取消了该任务,即使private async Task StartReceiveLoop(IMessageReceiver receiver, CancellationToken cancellationToken) { var doneReceiving = new TaskCompletionSource<bool>(); cancellationToken.Register(() => { receiver.CloseAsync(); doneReceiving.SetResult(true); }); receiver.RegisterMessageHandler( async (message, ct) => await HandleMessage(receiver, message), new MessageHandlerOptions(HandleException)); await doneReceiving.Task; } 仍在运行,该服务也会立即终止。

我可以通过库本身检查该操作是否仍在运行以延迟任务取消吗?我可以想到一种可以对所有正在运行的任务进行锁定来进行自己的计数的方法,但是我希望有一种更好的方法可以让我知道正在运行的处理程序的数量。

理想情况下,我想注销处理程序,以便消息泵停止,而接收器本身未关闭以允许例如CompleteAsync调用。

2 个答案:

答案 0 :(得分:2)

MessageReceiver.CloseAsync()如下:

  

关闭客户端。关闭由它打开的连接。

根据我的测试,在调用MessageReceiver.CloseAsync()之后,由于CompleteAsync实例已被处置,因此后续调用DeadLetterAsyncIMessageReceiver将失败。如果您仍然想完成队列消息,则需要创建一个新的MessageReceiver

  

我可以通过库本身检查该操作是否仍在运行以延迟任务取消吗?

AFAIK,SDK当前不提供上述功能。此外,这里有一个类似的feedback关于正常关闭Azure服务总线的消息泵。

  

服务停止后,我想给所有机会在进程终止之前完成所有操作。

根据您的要求,我假设您需要自己实现它,以确保即使在关闭MessageReceiver之后,也可以成功处理接收到的队列消息。或者,您可以只将CancellationToken参数传递到HandleMessage方法中以进行显式取消,而不用完成检索到的消息。

答案 1 :(得分:0)

另一个答案表明,不幸的是现在无法实现,并且即使该库本身不可用,该功能也受到了追捧。一种替代方法是创建您自己的接收消息泵,但是您可以自行进行断开连接,管理等操作,尽管正常关机本身并不难。使用目前的方法,我设法编写了一种变通方法,即使它很笨拙,也能正常工作。

private async Task StartReceiveLoop(IMessageReceiver receiver, CancellationToken cancellationToken)
{
    int activeMessageHandlersCount = 0;
    var doneReceiving = new TaskCompletionSource<bool>();

    cancellationToken.Register(() =>
    {
        lock (receiver)
        {
            int attemptCount = 0;
            while (attemptCount++ < 10 && activeMessageHandlersCount > 0)
            {
                Thread.Sleep(1000);
            }

            receiver.CloseAsync();
        }

        doneReceiving.SetResult(true);
    });

    receiver.RegisterMessageHandler(
        async (message, ct) =>
        {
            bool canBeProcessed;
            lock (receiver)
            {
                canBeProcessed = !cancellationToken.IsCancellationRequested;
                if (canBeProcessed)
                {
                    Interlocked.Increment(ref activeMessageHandlersCount);
                }
            }

            if (canBeProcessed)
            {
                try
                {
                    await HandleMessage(receiver, message);
                }
                finally
                {
                    Interlocked.Decrement(ref activeMessageHandlersCount);
                }
            }
            else
            {
                await Task.Delay(60000); // Otherwise message receiver will keep pumping message during graceful shutdown
            }
        }, new MessageHandlerOptions(HandleException));

    await doneReceiving.Task;
}

另一个缺点是Receiver会接收大量消息而不进行处理,只是保留它们直到锁到期。