【问题标题】:Create delay between two message reads of a Queue?在队列的两次消息读取之间创建延迟?
【发布时间】:2017-06-02 14:07:35
【问题描述】:

我正在使用 Azure 队列执行批量导入。 我正在使用WebJobs 在后台执行该过程。 队列出队非常频繁。如何在 2 条消息之间创建延迟 读?

这就是我向队列添加消息的方式

public async Task<bool> Handle(CreateFileUploadCommand message)
{
    var queueClient = _queueService.GetQueueClient(Constants.Queues.ImportQueue);

    var brokeredMessage = new BrokeredMessage(JsonConvert.SerializeObject(new ProcessFileUploadMessage
    {
        TenantId = message.TenantId,
        FileExtension = message.FileExtension,
        FileName = message.Name,
        DeviceId = message.DeviceId,
        SessionId = message.SessionId,
        UserId = message.UserId,
        OutletId = message.OutletId,
        CorrelationId = message.CorrelationId,

    }))
    {
        ContentType = "application/json",
    };

    await queueClient.SendAsync(brokeredMessage);

    return true;
}

下面是WebJobs函数。

public class Functions
{
    private readonly IValueProvider _valueProvider;
    public Functions(IValueProvider valueProvider)
    {
        _valueProvider = valueProvider;
    }

    public async Task ProcessQueueMessage([ServiceBusTrigger(Constants.Constants.Queues.ImportQueue)] BrokeredMessage message,
    TextWriter logger)
    {

        var queueMessage = message.GetBody<string>();

        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri(_valueProvider.Get("ServiceBaseUri"));

            var stringContent = new StringContent(queueMessage, Encoding.UTF8, "application/json");

            var result = await client.PostAsync(RestfulUrls.ImportMenu.ProcessUrl, stringContent);

            if (result.IsSuccessStatusCode)
            {
                await message.CompleteAsync();
            }
            else
            {
                await message.AbandonAsync();
            }
        }
    }
}

【问题讨论】:

  • 有什么更新吗?如果您觉得我的回答有用/有帮助。请将其标记为答案,以便其他人可以从中受益。

标签: c# azure-webjobs azure-servicebus-queues azure-queues azure-storage-queues


【解决方案1】:

据我所知,azure webjobs sdk 在单个实例上启用并发处理(默认为 16)。

如果你运行你的 webjobs,它将读取 16 个队列消息(peeklock 并在函数成功完成时在消息上调用 Complete,或者调用 Abandon)并同时创建 16 个进程来执行触发函数。所以你觉得队列出队非常频繁。

如果您想在单个实例上禁用并发处理。

我建议你可以将 ServiceBusConfiguration 的 MessageOptions.MaxConcurrentCalls 设置为 1。

更多细节,您可以参考以下代码:

在program.cs中:

JobHostConfiguration config = new JobHostConfiguration();
ServiceBusConfiguration serviceBusConfig = new ServiceBusConfiguration();
serviceBusConfig.MessageOptions.MaxConcurrentCalls = 1;
config.UseServiceBus(serviceBusConfig);

JobHost host = new JobHost(config);
host.RunAndBlock();

如果您想在 2 次消息读取之间创建延迟,我建议您可以创建自定义 ServiceBusConfiguration.MessagingProvider。

它包含CompleteProcessingMessageAsync方法,该方法在job函数被调用后完成对指定消息的处理。

我建议你可以在 CompleteProcessingMessageAsync 中添加 thread.sleep 方法来实现延迟读取。

更多细节,你可以参考下面的代码示例:

CustomMessagingProvider.cs:

注意:我重写了 CompleteProcessingMessageAsync 方法代码。

 public class CustomMessagingProvider : MessagingProvider
    {
        private readonly ServiceBusConfiguration _config;

        public CustomMessagingProvider(ServiceBusConfiguration config)
            : base(config)
        {
            _config = config;
        }

        public override NamespaceManager CreateNamespaceManager(string connectionStringName = null)
        {
            // you could return your own NamespaceManager here, which would be used
            // globally
            return base.CreateNamespaceManager(connectionStringName);
        }

        public override MessagingFactory CreateMessagingFactory(string entityPath, string connectionStringName = null)
        {
            // you could return a customized (or new) MessagingFactory here per entity
            return base.CreateMessagingFactory(entityPath, connectionStringName);
        }

        public override MessageProcessor CreateMessageProcessor(string entityPath)
        {
            // demonstrates how to plug in a custom MessageProcessor
            // you could use the global MessageOptions, or use different
            // options per entity
            return new CustomMessageProcessor(_config.MessageOptions);
        }

        private class CustomMessageProcessor : MessageProcessor
        {
            public CustomMessageProcessor(OnMessageOptions messageOptions)
                : base(messageOptions)
            {
            }

            public override Task<bool> BeginProcessingMessageAsync(BrokeredMessage message, CancellationToken cancellationToken)
            {
                // intercept messages before the job function is invoked
                return base.BeginProcessingMessageAsync(message, cancellationToken);
            }

            public override async Task CompleteProcessingMessageAsync(BrokeredMessage message, FunctionResult result, CancellationToken cancellationToken)
            {
                if (result.Succeeded)
                {
                    if (!MessageOptions.AutoComplete)
                    {
                        // AutoComplete is true by default, but if set to false
                        // we need to complete the message
                        cancellationToken.ThrowIfCancellationRequested();


                        await message.CompleteAsync();

                        Console.WriteLine("Begin sleep");
                        //Sleep 5 seconds
                        Thread.Sleep(5000);
                        Console.WriteLine("Sleep 5 seconds");

                    }
                }
                else
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    await message.AbandonAsync();
                }
            }
        }
    }

Program.cs 主要方法:

 static void Main()
        {
            var config = new JobHostConfiguration();

            if (config.IsDevelopment)
            {
                config.UseDevelopmentSettings();
            }

            var sbConfig = new ServiceBusConfiguration
            {
                MessageOptions = new OnMessageOptions
                {
                    AutoComplete = false,
                    MaxConcurrentCalls = 1
                }
            };
            sbConfig.MessagingProvider = new CustomMessagingProvider(sbConfig);
            config.UseServiceBus(sbConfig);
            var host = new JobHost(config);

            // The following code ensures that the WebJob will be running continuously
            host.RunAndBlock();
        }

结果:

【讨论】:

  • 因为这是一个async 方法,您应该将Thread.Sleep() 替换为await Task.Delay()。所以底层线程并没有完全阻塞,同时可以处理其他任务。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-10
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
  • 1970-01-01
  • 1970-01-01
  • 2019-02-02
相关资源
最近更新 更多