Zpracování chyb v Durable Functions (Azure Functions)
Článek
Orchestrace Durable Function jsou implementovány v kódu a mohou 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:
Verze 4 programovacího modelu Node.js pro Azure Functions je obecně dostupná. 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 v3 a v4 najdete v průvodci migrací.
V následujících fragmentech kódu JavaScript (PM4) označuje 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. Můžete napsat kód pro zpracování chyb a kompenzaci, který vyhovuje vašim potřebám ve funkci orchestrátoru.
Představte si například následující funkci orchestrátoru, která převádí finanční 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 pro Durable Functions 2.x. Pro Durable Functions 1.x je nutné použít DurableOrchestrationContext místo IDurableOrchestrationContext. Další informace o rozdílech mezi verzemi najdete v článku o verzích Durable Functions.
[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í nevyvolají 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říznak použijte při vyvolání rutin, například Invoke-DurableActivity.
Před vyvoláním rutin nastavte proměnnou $ErrorActionPreference předvoleb na "Stop" funkci orchestrátoru.
Další informace o zpracování chyb v PowerShellu najdete v dokumentaci k PowerShellu Try-Catch-Finally .
@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 vyrovnává kredity zpět do zdrojového účtu.
Automatické opakování při selhání
Při volání funkcí aktivity nebo dílčí orchestrace můžete zadat zásady automatického opakování. Následující příklad se pokusí volat funkci až třikrát a mezi jednotlivými opakováními 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 pro Durable Functions 2.x. Pro Durable Functions 1.x je nutné použít DurableOrchestrationContext místo IDurableOrchestrationContext. Další informace o rozdílech mezi verzemi najdete v článku o verzích Durable Functions.
@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 aktivity v předchozím příkladu přebírá parametr pro konfiguraci zásady automatického opakování. Pro přizpůsobení zásad automatického opakování existuje několik možností:
Maximální počet pokusů: Maximální počet pokusů. Pokud je nastavená hodnota 1, nebude se opakovat.
První interval opakování: Doba čekání před prvním pokusem o opakování.
Koeficient zpětného odsud: Koeficient použitý k určení míry zvýšení zásady. Výchozí hodnota je 1.
Maximální interval opakování: Maximální doba čekání mezi opakovanými pokusy.
Časový limit opakování: Maximální doba strávená opakováním. Výchozí chování je opakovat po neomezenou dobu.
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é, když deklarativní zásady 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 do funkce 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 do funkce 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 do funkce 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 do funkce 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...
}
Časové limity funkcí
Volání funkce v rámci funkce orchestrátoru můžete chtít opustit, pokud dokončení trvá příliš dlouho. Správným způsobem, jak to udělat dnes, je vytvoření trvalého časovače s "libovolným" selektorem úkolů, jak je znázorněno v následujícím příkladu:
Předchozí příklady jazyka C# jsou pro Durable Functions 2.x. Pro Durable Functions 1.x je nutné použít DurableOrchestrationContext místo IDurableOrchestrationContext. Další informace o rozdílech mezi verzemi najdete v článku o verzích Durable Functions.
Tento mechanismus ve skutečnosti neukončí probíhající provádění funkce aktivity. Místo toho jednoduše umožňuje funkci orchestrátoru ignorovat výsledek a pokračovat. 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 .