繁体   English   中英

Azure 服务总线的 Azure Function App 延迟重试

[英]Azure Function App delay retry for azure service bus

首先让我解释一下我有什么。 我自己有一个带有 Azure Function App 的 Azure 服务总线。 服务总线设置为使用 SQL 过滤器将特定消息类型推送到特定主题。 然后使用我的 Azure Function App,这些将获取最新消息并进行处理。

一个基本的例子

1:我向我的 EmailAPI 发送请求

2:EmailAPI 然后将类型为“Email”的新消息推送到服务总线

3:SQL Filter然后看到类型是“Email”,并被放入Service Bux中的email Topic

4:EmailListener Azure Function 监控服务总线并通知新消息

5:收集服务总线消息并处理它(基本上只是使用提供的信息发送电子邮件)

现在假设由于某种原因 SMTP 服务器连接有点中断,有时我们在尝试发送电子邮件 (EmailListener) 时收到 TimeOutException。 当抛出异常时会发生什么,Function App EmailListener 将尝试立即再次发送,不用等待,它只会尝试再次发送。 它将总共执行 10 次,然后通知服务总线将消息放入死信队列。

我试图做的是当抛出异常(例如 TimeOutException)时,我们等待 X 时间,然后再尝试处理相同的消息。 我看了很多不同的帖子,谈论 host.json 并尝试设置这些设置,但这些都没有用。 我找到了一个解决方案,但是该解决方案要求您创建消息的克隆并将其推回服务总线并延迟处理时间。 如果 Azure 服务总线/功能应用程序可以自行处理重试,我宁愿不实施我自己的手动延迟系统。

我遇到的最大问题(这可能取决于我的理解)是谁的错? 是处理重试策略的服务总线设置,还是处理 X 时间后尝试重试的 Azure Function App。

我提供了一些代码,但我觉得代码并不能真正帮助解释我的问题。

// Pseudo code
public static class EmailListenerTrigger
{
    [FunctionName("EmailListenerTrigger")]
    public static void Run([ServiceBusTrigger("messages", "email", Connection = "ConnectionString")]string mySbMsg, TraceWriter log)
    {
           var emailLauncher = new EmailLauncher("SmtpAddress", "SmtpPort", "FromAddress");
           try
           {
               emailLauncher.SendServiceBusMessage(mySbMsg);
           }
           catch(Exception ex)
           {
               log.Info($"Audit Log: {mySbMsg}, Excpetion: {ex.message}");
           }
    }
}

参考一: https ://blog.kloud.com.au/2017/05/22/message-retry-patterns-in-azure-functions/(Thread.Sleep 似乎不是个好主意)

参考二: https ://github.com/Azure/azure-functions-host/issues/2192(手动实现重试)

参考三: https ://www.feval.ca/posts/function-queue-retry/(这个好像是我用topics的时候指的队列)

参考四: Azure Service Bus 能否延迟重试一条消息? (谈论推迟消息,但随后您需要手动将其从队列/主题中取出。)

您或许可以通过使用持久函数来解决您的问题。 例如,有一个内置方法CallActivityWithRetryAsync()可以在活动函数抛出异常时重试。

https://learn.microsoft.com/en-us/sandbox/functions-recipes/durable-diagnostics#calling-activity-functions-with-retry

你的流程可能是这样的:

  1. 服务总线触发功能。 这个启动了一个 Orchestrator 函数

  2. 编排器调用您的活动功能(使用上述方法)

  3. 您的电子邮件发送是在 Activity Function 中实现的,并且可以根据需要抛出异常

虽然没有对您想做的事情的本机支持,但无需进行大量自定义开发即可实现。 您基本上可以将服务总线输出绑定添加到您的 Azure 函数,该函数连接到您的函数从中使用消息的同一队列。 然后,使用自定义属性来跟踪重试次数。 下面是一个例子:

private static TimeSpan[] BackoffDurationsBetweenFailures = new[] { }; // add delays here

[FunctionName("retrying-poc")]
public async Task Run(
  [ServiceBusTrigger("myQueue")] Message rawRequest,
  IDictionary<string, object> userProperties,
  [ServiceBus("myQueue")] IAsyncCollector<Message> collector)
{
  var request = GetRequest(rawRequest);
  var retryCount = GetRetryCount(userProperties);
  var shouldRetry = false;

  try
  {
    await _unreliableService.Call(request);
  }
  catch (Exception ex)
  {
     // I don't retry if it is a timeout, but that's my own choice.
    shouldRetry = !(ex is TimeoutException) && retryCount < BackoffDurationsBetweenFailures.Length;
  }

  if (shouldRetry)
  {
    var retryMessage = new Message(rawRequest.Body);
    retryMessage.UserProperties.Add("RetryCount", retryCount + 1);
    retryMessage.ScheduledEnqueueTimeUtc = DateTime.UtcNow.Add(BackoffDurationsBetweenFailures[retryCount]);
    await collector.AddAsync(retryMessage);
  }
}


private MyBusinessObject GetRequest(Message rawRequest)
  => JsonConvert.DeserializeObject<MyBusinessObject>(Encoding.UTF8.GetString(rawRequest.Body));

private int GetRetryCount(IDictionary<string, object> properties)
  => properties.TryGetValue("RetryCount", out var value) && int.TryParse(value.ToString(), out var retryCount)
            ? retryCount
            : 0;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM