部署时多次调用Azure Service Bus队列触发器功能

时间:2020-11-10 22:31:29

标签: c# azure-functions azure-servicebus-queues

我有两个Azure函数。一种是HTTP触发的,我们称它为API,另一种是ServiceBusQueue触发的,我们称其为Listener

第一个(API)将HTTP请求放入队列,第二个(侦听器)将其提取并处理。函数SDK版本为:3.0.7

我的解决方案中有两个项目。一个包含Azure函数,另一个包含服务。 API一旦触发,就会从另一个项目中调用服务,该服务会将消息放入队列中。侦听器一旦收到一条消息,就会从服务项目中调用服务以处理该消息。

任何长时间运行的过程吗?

Listener实际上执行的是轻量级的工作流程,考虑到其执行的工作量,这一切很快发生。平均执行时间为90 seconds

队列规范是什么?

侦听器侦听并托管在Azure ServiceBus命名空间中的队列设置了以下属性:

  • 最大交付数:1
  • 消息生存时间:1天
  • 自动删除:从不
  • 重复检测窗口:10分钟
  • 消息锁定持续时间:5分钟

下面是它的屏幕截图:

enter image description here

API使用以下方法将HTTP请求放入队列:

 public async Task ProduceAsync(string queueName, string jsonMessage)
 {
        jsonMessage.NotNull();
        queueName.NotNull();

        IQueueClient client = new QueueClient(Environment.GetEnvironmentVariable("ServiceBusConnectionString"), queueName, ReceiveMode.PeekLock)
        {
            OperationTimeout = TimeSpan.FromMinutes(5)
        };

        await client.SendAsync(new Message(Encoding.UTF8.GetBytes(jsonMessage)));

        if (!client.IsClosedOrClosing)
        {
            await client.CloseAsync();
        }
 }

监听器(服务总线队列触发了azure函数)具有以下代码来处理消息:

[FunctionName(nameof(UpdateBookingCalendarListenerFunction))]
public async Task Run([ServiceBusTrigger(ServiceBusConstants.UpdateBookingQueue, Connection = ServiceBusConstants.ConnectionStringKey)] string message)
{
        var data = JsonConvert.DeserializeObject<UpdateBookingCalendarRequest>(message);
        _telemetryClient.TrackTrace($"{nameof(UpdateBookingCalendarListenerFunction)} picked up a message at {DateTime.Now}. Data: {data}");

        await _workflowHandler.HandleAsync(data);
}

问题

Listener函数处理同一条消息3次!而且我不知道为什么!我已经Google搜索并阅读了一些StackOverFlow线程,例如this one。似乎每个人都建议确保lock duration足够长,以使该过程完全执行。尽管我已将5 minutes用作锁,但是问题仍然存在。非常感谢您提供任何帮助。

1 个答案:

答案 0 :(得分:1)

只需在此处添加它,对其他一些人可能会有帮助。

经过更多调查后,我意识到在我的特定情况下,该问题与Azure功能和服务总线无关。在UpdateBookingCalendarListenerFunction向其发送消息的工作流处理程序中,我试图以并行方式调用某些外部API,但是由于(出于我未知的原因),处理程序代码又一次调用了外部API。 ,无论迭代多少记录。下面的代码显示了我是如何实现并行API调用的,而其他代码则显示了我是如何一步一步实现最终解决我所遇到问题的方法。

我的原始代码-并行调用API

 public async Task<IEnumerable<StaffMemberGraphApiResponse>> AddAdminsAsync(IEnumerable<UpdateStaffMember> admins, string bookingId)
    {
        var apiResults = new List<StaffMemberGraphApiResponse>();

        var adminsToAdd = admins.Where(ad => ad.Action == "add");

        _telemetryClient.TrackTrace($"{nameof(UpdateBookingCalendarWorkflowDetailHandler)} Recognized {adminsToAdd.Count()} admins to add to booking with id: {bookingId}");

        var addAdminsTasks = adminsToAdd.Select(admin => _addStaffGraphApiHandler.HandleAsync(new AddStaffToBookingGraphApiRequest
        {
            BookingId = bookingId,
            DisplayName = admin.DisplayName,
            EmailAddress = admin.EmailAddress,
            Role = StaffMemberAllowedRoles.Admin
        }));

        if (addAdminsTasks.Any())
        {
            var addAdminsTasksResults = await Task.WhenAll(addAdminsTasks);
            apiResults = _populateUpdateStaffMemberResponse.Populate(addAdminsTasksResults, StaffMemberAllowedRoles.Admin).ToList();
        }

        return apiResults;
    }

我的新代码没有将API调用汇总到addAdminsTasks对象中,因此没有await Task.WhenAll(addAdminsTasks)

 public async Task<IEnumerable<StaffMemberGraphApiResponse>> AddStaffMembersAsync(IEnumerable<UpdateStaffMember> members, string bookingId, string targetRole)
    {
        var apiResults = new List<StaffMemberGraphApiResponse>();

        foreach (var item in members.Where(v => v.Action == "add"))
        {
            _telemetryClient.TrackTrace($"{nameof(UpdateBookingCalendarWorkflowDetailHandler)} Adding {targetRole} to booking: {bookingId}. data: {JsonConvert.SerializeObject(item)}");

            apiResults.Add(_populateUpdateStaffMemberResponse.PopulateAsSingleItem(await _addStaffGraphApiHandler.HandleAsync(new AddStaffToBookingGraphApiRequest
            {
                BookingId = bookingId,
                DisplayName = item.DisplayName,
                EmailAddress = item.EmailAddress,
                Role = targetRole
            }), targetRole));
        }

        return apiResults;
    }

我已经研究了第一种方法,任务数量与IEnumerable输入的数量完全匹配,但是API又被称为一次。在_addStaffGraphApiHandler.HandleAsync中,实际上没有什么引发HttpClient请求的POST对象。无论如何,使用第二个代码已解决了该问题。