![](/img/trans.png)
[英]Is there a way I can delay the retry for a service bus message in an Azure function?
[英]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()
可以在活動函數拋出異常時重試。
你的流程可能是這樣的:
服務總線觸發功能。 這個啟動了一個 Orchestrator 函數
編排器調用您的活動功能(使用上述方法)
雖然沒有對您想做的事情的本機支持,但無需進行大量自定義開發即可實現。 您基本上可以將服務總線輸出綁定添加到您的 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.