Zpracování chyb v Durable Functions (Azure Functions)
Článek
Orchestrace Durable Function se implementují v kódu a můžou používat integrované funkce zpracování chyb programovacího jazyka. Ve skutečnosti nejsou k dispozici žádné nové koncepty, které byste se měli naučit přidávat do orchestrací zpracování chyb a kompenzaci. Existuje však několik chování, o které byste měli vědět.
Poznámka
Obecně dostupná je verze 4 programovacího modelu Node.js pro Azure Functions. Nový model v4 je navržený tak, aby měl flexibilnější a intuitivnější prostředí pro vývojáře v JavaScriptu a TypeScriptu. Další informace o rozdílech mezi verzemi v3 a v4 najdete v průvodci migrací.
V následujících fragmentech kódu označuje JavaScript (PM4) programovací model V4, nové prostředí.
Chyby ve funkcích aktivit
Jakákoli výjimka, která je vyvolána ve funkci aktivity, je zařazována zpět do funkce orchestrátoru a vyvolána jako FunctionFailedException. Ve funkci orchestrátoru můžete napsat kód pro zpracování chyb a kompenzaci, který bude vyhovovat vašim potřebám.
Představte si například následující funkci orchestrátoru, která převádí prostředky z jednoho účtu na jiný:
[FunctionName("TransferFunds")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var transferDetails = context.GetInput<TransferOperation>();
await context.CallActivityAsync("DebitAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
try
{
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.DestinationAccount,
Amount = transferDetails.Amount
});
}
catch (Exception)
{
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
}
}
Poznámka
Předchozí příklady jazyka C# jsou určené pro Durable Functions 2.x. Pro Durable Functions 1.x musíte místo IDurableOrchestrationContextpoužít DurableOrchestrationContext . Další informace o rozdílech mezi verzemi najdete v článku Durable Functions verze.
[FunctionName("TransferFunds")]
public static async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext context, TransferOperation transferDetails)
{
await context.CallActivityAsync("DebitAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
try
{
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.DestinationAccount,
Amount = transferDetails.Amount
});
}
catch (Exception)
{
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
}
}
const df = require("durable-functions");
module.exports = df.orchestrator(function* (context) {
const transferDetails = context.df.getInput();
yield context.df.callActivity("DebitAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
try {
yield context.df.callActivity("CreditAccount", {
account: transferDetails.destinationAccount,
amount: transferDetails.amount,
});
} catch (error) {
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
yield context.df.callActivity("CreditAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
}
})
const df = require("durable-functions");
df.app.orchestration("transferFunds", function* (context) {
const transferDetails = context.df.getInput();
yield context.df.callActivity("debitAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
try {
yield context.df.callActivity("creditAccount", {
account: transferDetails.destinationAccount,
amount: transferDetails.amount,
});
} catch (error) {
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
yield context.df.callActivity("creditAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
}
});
Rutiny v PowerShellu ve výchozím nastavení nevyvolávejte výjimky, které je možné zachytit pomocí bloků try/catch. Toto chování můžete změnit dvěma způsoby:
-ErrorAction Stop Při vyvolání rutin, jako Invoke-DurableActivityje .
$ErrorActionPreference Před vyvoláním rutin nastavte proměnnou předvoleb ve funkci orchestrátoru na "Stop" hodnotu .
@FunctionName("TransferFunds")
public void transferFunds(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
TransferOperation transfer = ctx.getInput(TransferOperation.class);
ctx.callActivity(
"DebitAccount",
new OperationArgs(transfer.sourceAccount, transfer.amount)).await();
try {
ctx.callActivity(
"CreditAccount",
new OperationArgs(transfer.destinationAccount, transfer.amount)).await();
} catch (TaskFailedException ex) {
// Refund the source account on failure
ctx.callActivity(
"CreditAccount",
new OperationArgs(transfer.sourceAccount, transfer.amount)).await();
}
}
Pokud první volání funkce CreditAccount selže, funkce orchestrátoru je kompenzuje tím, že finanční prostředky připíše zpět na zdrojový účet.
Automatické opakování při selhání
Při volání funkcí aktivit nebo dílčí orchestrace můžete zadat zásadu automatického opakování. Následující příklad se pokusí volat funkci až třikrát a mezi každým opakováním počká 5 sekund:
[FunctionName("TimerOrchestratorWithRetry")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: 3);
await context.CallActivityWithRetryAsync("FlakyFunction", retryOptions, null);
// ...
}
Poznámka
Předchozí příklady jazyka C# jsou určené pro Durable Functions 2.x. Pro Durable Functions 1.x musíte místo IDurableOrchestrationContextpoužít DurableOrchestrationContext . Další informace o rozdílech mezi verzemi najdete v článku Durable Functions verze.
@FunctionName("TimerOrchestratorWithRetry")
public void timerOrchestratorWithRetry(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
final int maxAttempts = 3;
final Duration firstRetryInterval = Duration.ofSeconds(5);
RetryPolicy policy = new RetryPolicy(maxAttempts, firstRetryInterval);
TaskOptions options = new TaskOptions(policy);
ctx.callActivity("FlakeyFunction", options).await();
// ...
}
Volání funkce activity v předchozím příkladu přebírá parametr pro konfiguraci zásad automatického opakování. Zásady automatického opakování můžete přizpůsobit několika způsoby:
Maximální počet pokusů: Maximální počet pokusů. Pokud je nastavená hodnota 1, nebude se opakovat žádný pokus.
Interval prvního opakování: Doba čekání před prvním opakovaným pokusem.
Koeficient zásady: Koeficient použitý k určení míry nárůstu zásady. Výchozí hodnota je 1.
Maximální interval opakování: Maximální doba čekání mezi opakovanými pokusy.
Vypršení časového limitu opakování: Maximální doba potřebná k opakování. Výchozím chováním je opakování neomezeně dlouho.
Vlastní obslužné rutiny opakování
Při použití .NET nebo Javy máte také možnost implementovat obslužné rutiny opakování v kódu. To je užitečné v případě, že zásady deklarativního opakování nejsou dostatečně výrazné. U jazyků, které nepodporují vlastní obslužné rutiny opakování, máte stále možnost implementovat zásady opakování pomocí smyček, zpracování výjimek a časovačů pro vkládání zpoždění mezi opakováními.
RetryOptions retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: int.MaxValue)
{
Handle = exception =>
{
// True to handle and try again, false to not handle and throw.
if (exception is TaskFailedException failure)
{
// Exceptions from TaskActivities are always this type. Inspect the
// inner Exception to get more details.
}
return false;
};
}
await ctx.CallActivityWithRetryAsync("FlakeyActivity", retryOptions, null);
TaskOptions retryOptions = TaskOptions.FromRetryHandler(retryContext =>
{
// Don't retry anything that derives from ApplicationException
if (retryContext.LastFailure.IsCausedBy<ApplicationException>())
{
return false;
}
// Quit after N attempts
return retryContext.LastAttemptNumber < 3;
});
try
{
await ctx.CallActivityAsync("FlakeyActivity", options: retryOptions);
}
catch (TaskFailedException)
{
// Case when the retry handler returns false...
}
JavaScript v současné době nepodporuje vlastní obslužné rutiny opakování. Stále ale máte možnost implementovat logiku opakování přímo ve funkci orchestrátoru pomocí smyček, zpracování výjimek a časovačů pro vkládání zpoždění mezi opakováními.
JavaScript v současné době nepodporuje vlastní obslužné rutiny opakování. Stále ale máte možnost implementovat logiku opakování přímo ve funkci orchestrátoru pomocí smyček, zpracování výjimek a časovačů pro vkládání zpoždění mezi opakováními.
Python v současné době nepodporuje vlastní obslužné rutiny opakování. Stále ale máte možnost implementovat logiku opakování přímo ve funkci orchestrátoru pomocí smyček, zpracování výjimek a časovačů pro vkládání zpoždění mezi opakováními.
PowerShell v současné době nepodporuje vlastní obslužné rutiny opakování. Stále ale máte možnost implementovat logiku opakování přímo ve funkci orchestrátoru pomocí smyček, zpracování výjimek a časovačů pro vkládání zpoždění mezi opakováními.
RetryHandler retryHandler = retryCtx -> {
// Don't retry anything that derives from RuntimeException
if (retryCtx.getLastFailure().isCausedBy(RuntimeException.class)) {
return false;
}
// Quit after N attempts
return retryCtx.getLastAttemptNumber() < 3;
};
TaskOptions options = new TaskOptions(retryHandler);
try {
ctx.callActivity("FlakeyActivity", options).await();
} catch (TaskFailedException ex) {
// Case when the retry handler returns false...
}
Vypršení časových limitů funkcí
Volání funkce v rámci funkce orchestrátoru můžete chtít opustit, pokud jeho dokončení trvá příliš dlouho. Správný způsob, jak to udělat dnes, je vytvořit trvalý časovač s libovolným selektorem úloh, jako v následujícím příkladu:
Předchozí příklady jazyka C# jsou určené pro Durable Functions 2.x. Pro Durable Functions 1.x musíte místo IDurableOrchestrationContextpoužít DurableOrchestrationContext . Další informace o rozdílech mezi verzemi najdete v článku Durable Functions verze.
Tento mechanismus ve skutečnosti neukončí provádění probíhajících funkcí aktivity. Místo toho jednoduše umožňuje funkci orchestrátoru ignorovat výsledek a pokračovat dál. Další informace najdete v dokumentaci k časovačům .
Neošetřené výjimky
Pokud funkce orchestrátoru selže s neošetřenou výjimkou, zaprotokolují se podrobnosti o výjimce a instance se dokončí se stavem Failed .