Durable Functions per workflow con stato
Orchestri workflow di lunga durata usando i pattern degli orchestrator di Durable Functions (fan-out/fan-in, chaining, monitor) e comprenda come viene salvato lo stato tramite checkpoint.
Durable Functions per workflow con stato è una lezione Azure Fundamentals gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Azure Fundamentals, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Azure Fundamentals include 4 lezioni in totale.
Perché Durable Functions?
Le normali Azure Functions sono senza stato: ogni chiamata viene eseguita in modo indipendente e non conserva memoria delle chiamate precedenti. Durable Functions estende Azure Functions con la possibilità di scrivere flussi di lavoro con stato e di lunga durata utilizzando semplice codice async/await. Durable Task Framework salva automaticamente dei checkpoint dello stato in Azure Storage dopo ogni passaggio, così i flussi di lavoro possono sopravvivere a riavvii del server, timeout o interventi di manutenzione pianificati e riprendere esattamente dal punto in cui erano stati interrotti.
I tre tipi di funzioni
Durable Functions introduce tre tipi di funzioni. Una funzione orchestrator coordina il flusso di lavoro complessivo: chiama le funzioni activity e attende i risultati utilizzando yield o await, senza bloccare effettivamente un thread. Una funzione activity esegue una singola unità di lavoro (chiamare un'API, scrivere in un database) ed è l'unico punto in cui devono verificarsi effetti collaterali. Una funzione entity mantiene piccole porzioni di stato persistente (contatori, flag) tra una chiamata e l'altra.
// Client function (HTTP trigger) — starts the orchestration
module.exports = async function (context, req) {
const client = df.getClient(context);
const orderId = req.body.orderId;
const instanceId = await client.startNew('OrderOrchestrator', undefined, { orderId });
return client.createCheckStatusResponse(context.bindingData.req, instanceId);
};Il modello di concatenamento
Il modello di concatenamento esegue le funzioni activity in sequenza, passando l'output di una come input della successiva. L'orchestrator attende ogni activity a turno. Se un'activity ha esito negativo, il flusso di lavoro si interrompe e può essere riavviato dal passaggio non riuscito. Questo è il modello più semplice di Durable Functions ed è adatto ai flussi di lavoro in cui ogni passaggio dipende dal risultato di quello precedente, ad esempio nelle pipeline di elaborazione degli ordini.
// Orchestrator: chaining pattern
const df = require('durable-functions');
module.exports = df.orchestrator(function* (context) {
const orderId = context.df.getInput().orderId;
const validated = yield context.df.callActivity('ValidateOrder', orderId);
const charged = yield context.df.callActivity('ChargePayment', validated);
const shipped = yield context.df.callActivity('ShipOrder', charged);
return { status: 'shipped', trackingId: shipped.trackingId };
});Modello fan-out/fan-in
Il modello fan-out/fan-in avvia più funzioni activity in parallelo e attende che tutte siano completate prima di continuare. L'orchestrator avvia simultaneamente tutte le attività utilizzando callActivity senza attenderle, raccoglie gli oggetti task in un array e quindi esegue lo yield su Task.all(). Per elementi di lavoro indipendenti, come l'elaborazione di più file, le chiamate a più API o le operazioni batch, questo approccio è molto più veloce dell'elaborazione sequenziale.
// Orchestrator: fan-out / fan-in
module.exports = df.orchestrator(function* (context) {
const items = context.df.getInput().items;
// Fan-out: start all tasks in parallel
const tasks = items.map(item => context.df.callActivity('ProcessItem', item));
// Fan-in: wait for all tasks to complete
const results = yield context.df.Task.all(tasks);
return results;
});Il modello di monitoraggio
Il modello di monitoraggio esegue il polling di un sistema esterno a intervalli regolari finché non viene soddisfatta una condizione, in modo simile a un ciclo di polling, ma con persistenza completa. L'orchestrator chiama un'activity per verificare lo stato, attende un intervallo configurabile utilizzando createTimer e quindi ripete il ciclo. Poiché lo stato viene salvato in un checkpoint nello storage tra un'interrogazione e l'altra, l'orchestrator non consuma risorse di calcolo durante l'attesa, risultando molto più efficiente di un approccio basato su un timer in sospensione.
// Orchestrator: monitor pattern (poll until job completes)
module.exports = df.orchestrator(function* (context) {
const jobId = context.df.getInput().jobId;
const expiry = new Date(context.df.currentUtcDateTime);
expiry.setHours(expiry.getHours() + 24); // 24-hour timeout
while (context.df.currentUtcDateTime < expiry) {
const status = yield context.df.callActivity('GetJobStatus', jobId);
if (status === 'completed') return { jobId, status };
if (status === 'failed') throw new Error('Job failed');
// Wait 30 seconds before next poll
const nextCheck = new Date(context.df.currentUtcDateTime);
nextCheck.setSeconds(nextCheck.getSeconds() + 30);
yield context.df.createTimer(nextCheck);
}
throw new Error('Workflow timed out');
});Modello di interazione umana
Il modello di interazione umana mette in pausa un'orchestrazione e attende un evento esterno, ad esempio l'approvazione di un responsabile. L'orchestrator esegue lo yield su waitForExternalEvent, che può attendere per giorni o settimane senza consumare risorse di calcolo. Un sistema esterno (un collegamento di approvazione via e-mail, un'app mobile o un webhook) chiama l'API HTTP di Durable Functions per generare l'evento, sbloccando l'orchestrazione. Combini questo modello con un timer per gestire automaticamente timeout ed escalation se non arriva alcuna risposta.
// Orchestrator: wait for human approval with timeout
module.exports = df.orchestrator(function* (context) {
const request = context.df.getInput();
yield context.df.callActivity('SendApprovalEmail', request);
const timeout = df.Task.createTimer(context, new Date(Date.now() + 48 * 3600 * 1000));
const approval = context.df.waitForExternalEvent('ApprovalResponse');
const winner = yield context.df.Task.any([approval, timeout]);
if (winner === approval) {
const approved = winner.result;
return approved ? 'Approved' : 'Rejected';
} else {
return 'Timed out — escalated';
}
});Vincoli degli orchestrator
Le funzioni orchestrator sono soggette a vincoli importanti perché possono essere rieseguite più volte a partire dalla cronologia per ricostruire lo stato. Devono essere deterministiche: non utilizzi mai Date.now() o Math.random() e non esegua chiamate I/O dirette. Utilizzi invece context.df.currentUtcDateTime per i timestamp e chiami le funzioni activity per tutte le operazioni I/O. La registrazione nel corpo dell'orchestrator produrrà voci di log duplicate durante la riesecuzione; utilizzi invece le funzioni activity per la registrazione.
// WRONG — non-deterministic, will cause replay bugs
module.exports = df.orchestrator(function* (context) {
const now = new Date(); // Don't use Date()
const rand = Math.random(); // Don't use Math.random()
const data = await fetch('/api'); // Don't make HTTP calls directly
});
// CORRECT
module.exports = df.orchestrator(function* (context) {
const now = context.df.currentUtcDateTime; // OK
const data = yield context.df.callActivity('FetchData', null); // OK
});Gestione delle istanze: stato e terminazione
Ogni esecuzione di un'orchestrazione ha un ID istanza univoco che può essere utilizzato per interrogarne lo stato, inviare eventi o terminarla. L'API HTTP di gestione di Durable Functions fornisce endpoint per controllare lo stato (GET /instances/{id}), inviare eventi (POST /instances/{id}/raiseEvent/{name}) e terminare l'istanza (POST /instances/{id}/terminate). Utilizzi il binding del client Durable nelle funzioni per accedere programmaticamente a queste operazioni.
// Client function: check orchestration status
module.exports = async function (context, req) {
const client = df.getClient(context);
const instanceId = req.params.instanceId;
const status = await client.getStatus(instanceId, true, true, true);
return {
status: 200,
body: {
instanceId,
runtimeStatus: status.runtimeStatus,
customStatus: status.customStatus,
output: status.output
}
};
};Backend di storage e prestazioni
Durable Functions archivia la cronologia dell'orchestrazione, lo stato delle istanze e le code di messaggi tra funzioni in un account Azure Storage (oppure nei backend Azure SQL/Netherite per ottenere un throughput maggiore). Ogni checkpoint scrive in Azure Table Storage e Azure Queue Storage. Negli scenari ad alto throughput (migliaia di orchestrazioni simultanee), il backend di storage Netherite utilizza Azure Event Hubs per offrire prestazioni nettamente superiori. Monitori la profondità della coda delle orchestrazioni per rilevare eventuali colli di bottiglia.
// host.json: configure the Durable Task storage provider
{
'version': '2.0',
'extensions': {
'durableTask': {
'hubName': 'MyTaskHub',
'storageProvider': {
'type': 'azure',
'connectionStringName': 'AzureWebJobsStorage',
'controlQueueBatchSize': 32,
'maxQueuePollingInterval': '00:00:02'
}
}
}
}Gestione degli errori e nuovi tentativi
Le funzioni activity possono generare eccezioni, che vengono propagate all'orchestrator come TaskFailedException. Utilizzi blocchi try-catch nell'orchestrator per gestire gli errori in modo appropriato. Per gli errori temporanei, configuri nuovi tentativi automatici con backoff utilizzando callActivityWithRetry, specificando il numero massimo di tentativi, l'intervallo del primo nuovo tentativo e il coefficiente di backoff. Questo è il modello consigliato per le activity che chiamano API o database esterni.
// Orchestrator: retry an activity with exponential backoff
module.exports = df.orchestrator(function* (context) {
const retryOptions = new df.RetryOptions(
5000, // firstRetryIntervalInMilliseconds
3 // maxNumberOfAttempts
);
retryOptions.backoffCoefficient = 2; // 5s, 10s, 20s
try {
const result = yield context.df.callActivityWithRetry(
'CallExternalAPI',
retryOptions,
context.df.getInput()
);
return result;
} catch (e) {
yield context.df.callActivity('SendFailureAlert', e.message);
throw e;
}
});Entità Durable
Durable Entities (funzioni entity) implementa piccole porzioni di stato persistente accessibili tramite un'identità, in modo simile agli attori virtuali. Un'entità ha un ID e uno stato che persiste tra una chiamata e l'altra. È possibile chiamare operazioni su un'entità da un orchestrator o da un client; l'entità le elabora una alla volta (in modo serializzato). Gli utilizzi comuni includono contatori, macchine a stati per le approvazioni, limitatori di frequenza e carrelli della spesa: qualsiasi scenario che richieda uno stato persistente e aggiornabile senza un database.
// Counter entity function
const df = require('durable-functions');
module.exports = df.entity(function (context) {
let count = context.df.getState(() => 0);
const operation = context.df.operationName;
if (operation === 'add') count += context.df.getInput();
if (operation === 'reset') count = 0;
if (operation === 'get') context.df.return(count);
context.df.setState(count);
});
// From orchestrator, increment counter entity
// const entityId = new df.EntityId('Counter', 'myCounter');
// yield context.df.callEntity(entityId, 'add', 1);Verifica rapida
Verifichi la Sua comprensione dei concetti di Microsoft Azure Fundamentals (AZ-900) trattati in questa lezione.
Riepilogo della lezione
In questa lezione ha imparato che Durable Functions consente di creare flussi di lavoro con stato e di lunga durata salvando i checkpoint dello stato dell'orchestrator in Azure Storage; i modelli principali includono concatenamento, fan-out/fan-in, monitoraggio e interazione umana; inoltre, gli orchestrator devono essere deterministici: tutte le operazioni I/O e le chiamate non deterministiche devono passare attraverso le funzioni activity. Nel prossimo argomento esploreremo Azure Logic Apps.
Domande Frequenti
La lezione «Durable Functions per workflow con stato» è gratuita?
Sì — il testo completo di «Durable Functions per workflow con stato» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Azure Fundamentals, passa a CoddyKit PRO. Il corso Azure Fundamentals include 4 lezioni in totale.
Cosa imparerò in «Durable Functions per workflow con stato»?
Orchestri workflow di lunga durata usando i pattern degli orchestrator di Durable Functions (fan-out/fan-in, chaining, monitor) e comprenda come viene salvato lo stato tramite checkpoint. Eserciti Azure Fundamentals con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Azure Fundamentals?
Non è richiesta alcuna esperienza precedente. Azure Fundamentals su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Durable Functions per workflow con stato»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Azure Fundamentals?
Sì. Ogni lezione Azure Fundamentals include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Trigger e binding di Azure Functions
- Durable Functions per workflow con stato
- Azure Logic Apps
- Event Grid e architettura event-driven