A Durable Functions hibáinak kezelése (Azure Functions)
Cikk
A durable function vezénylések kódban vannak implementálva, és használhatják a programozási nyelv beépített hibakezelési funkcióit. A hibák kezelésének és a kompenzációnak a vezénylésekhez való hozzáadásához nincs szükség új fogalmakra. Vannak azonban olyan viselkedések, amelyeket érdemes figyelembe vennie.
Megjegyzés
A Azure Functions Node.js programozási modelljének 4. verziója általánosan elérhető. Az új v4-modell rugalmasabb és intuitívabb felhasználói élményt nyújt JavaScript- és TypeScript-fejlesztők számára. A migrálási útmutatóban további információt talál a v3 és a v4 közötti különbségekről.
A következő kódrészletekben a JavaScript (PM4) az új V4 programozási modellt jelöli.
Tevékenységfüggvények hibái
A tevékenységfüggvényekben megjelenő kivételeket a rendszer visszaállítja a vezénylő függvénybe, és egyként FunctionFailedExceptiondobja ki. A vezénylő függvényben az igényeinek megfelelő hibakezelési és kompenzációs kódot írhat.
Vegyük például a következő vezénylő függvényt, amely az egyik fiókból a másikba utal át pénzt:
[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
});
}
}
Megjegyzés
Az előző C#-példák a 2.x Durable Functions. Az 1.x Durable Functions helyett a következőt kell használnia DurableOrchestrationContextIDurableOrchestrationContext: . A verziók közötti különbségekről további információt a Durable Functions verziókról szóló cikkben talál.
[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,
});
}
});
Alapértelmezés szerint a PowerShell-parancsmagok nem emelnek kivételeket, amelyek a try/catch blokkok használatával elkaphatók. Kétféleképpen módosíthatja ezt a viselkedést:
Használja a -ErrorAction Stop jelölőt parancsmagok, például Invoke-DurableActivitya parancsmagok invokálásakor.
A parancsmagok meghívása előtt állítsa a $ErrorActionPreference beállítási változót "Stop" a vezénylő függvényre.
A PowerShell-hibakezeléssel kapcsolatos további információkért lásd a Try-Catch-Finally PowerShell dokumentációt.
@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();
}
}
Ha az első CreditAccount függvényhívás meghiúsul, a vezénylő függvény kompenzálja az összeget a forrásfiókba történő jóváírással.
Automatikus újrapróbálkozás hiba esetén
Amikor tevékenységfüggvényeket vagy alvezénylési függvényeket hív meg, megadhat egy automatikus újrapróbálkozási szabályzatot. Az alábbi példa legfeljebb háromszor próbál meghívni egy függvényt, és minden újrapróbálkozás között 5 másodpercet vár:
[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);
// ...
}
Megjegyzés
Az előző C#-példák a 2.x Durable Functions. Az 1.x Durable Functions helyett a következőt kell használnia DurableOrchestrationContextIDurableOrchestrationContext: . A verziók közötti különbségekről további információt a Durable Functions verziókról szóló cikkben talál.
@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();
// ...
}
Az előző példában a tevékenységfüggvény hívása egy paramétert vesz igénybe az automatikus újrapróbálkozási szabályzat konfigurálásához. Az automatikus újrapróbálkozási szabályzat testreszabására több lehetőség is van:
Kísérletek maximális száma: A kísérletek maximális száma. Ha az 1 értékre van állítva, nem lesz újrapróbálkozás.
Első újrapróbálkozási időköz: Az első újrapróbálkozási kísérlet előtti várakozási idő.
Visszalépési együttható: A visszalépés növekedésének mértékének meghatározására használt együttható. Alapértelmezés szerint 1.
Maximális újrapróbálkozási időköz: Az újrapróbálkozási kísérletek közötti várakozás maximális időtartama.
Újrapróbálkozás időtúllépése: Az újrapróbálkozással töltött idő maximális száma. Az alapértelmezett viselkedés az újrapróbálkozás határozatlan ideig.
Egyéni újrapróbálkozás-kezelők
A .NET vagy a Java használatakor lehetősége van újrapróbálkozás-kezelők implementálására is a kódban. Ez akkor hasznos, ha a deklaratív újrapróbálkozási szabályzatok nem elég kifejezőek. Az egyéni újrapróbálkozási kezelőket nem támogató nyelvek esetében továbbra is alkalmazhat újrapróbálkozási szabályzatokat hurkok, kivételkezelés és időzítők használatával az újrapróbálkozások közötti késések injektálására.
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...
}
A JavaScript jelenleg nem támogatja az egyéni újrapróbálkozás-kezelőket. Azonban továbbra is lehetősége van az újrapróbálkozási logika implementálására közvetlenül a vezénylő függvényben hurkok, kivételkezelés és időzítők használatával az újrapróbálkozások közötti késések injektálására.
A JavaScript jelenleg nem támogatja az egyéni újrapróbálkozás-kezelőket. Azonban továbbra is lehetősége van az újrapróbálkozási logika implementálására közvetlenül a vezénylő függvényben hurkok, kivételkezelés és időzítők használatával az újrapróbálkozások közötti késések injektálására.
A Python jelenleg nem támogatja az egyéni újrapróbálkozás-kezelőket. Azonban továbbra is lehetősége van az újrapróbálkozási logika implementálására közvetlenül a vezénylő függvényben hurkok, kivételkezelés és időzítők használatával az újrapróbálkozások közötti késések injektálására.
A PowerShell jelenleg nem támogatja az egyéni újrapróbálkozás-kezelőket. Azonban továbbra is lehetősége van az újrapróbálkozási logika implementálására közvetlenül a vezénylő függvényben hurkok, kivételkezelés és időzítők használatával az újrapróbálkozások közötti késések injektálására.
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...
}
Függvény időtúllépései
Előfordulhat, hogy egy vezénylőfüggvényen belül fel szeretné hagyni a függvényhívást, ha az túl sokáig tart. Ennek ma a megfelelő módja egy tartós időzítő létrehozása egy "bármely" feladatválasztóval, az alábbi példához hasonlóan:
Az előző C#-példák a 2.x Durable Functions. Az 1.x Durable Functions helyett a következőt kell használnia DurableOrchestrationContextIDurableOrchestrationContext: . A verziók közötti különbségekről további információt a Durable Functions verziókról szóló cikkben talál.
Ez a mechanizmus valójában nem állítja le a folyamatban lévő tevékenységfüggvények végrehajtását. Ehelyett egyszerűen lehetővé teszi, hogy a vezénylő függvény figyelmen kívül hagyja az eredményt, és továbblépjen. További információt az Időzítők dokumentációjában talál.
Nem kezelt kivételek
Ha egy vezénylőfüggvény kezeletlen kivétellel meghiúsul, a rendszer naplózza a kivétel részleteit, és a példány állapottal Failed fejeződik be.