Trigger e binding: due concetti distinti

In Azure Functions ogni funzione nasce da un trigger e da esso soltanto: è l’evento che ne provoca l’esecuzione e ne definisce il payload iniziale. La regola cardine è che una funzione ha esattamente un trigger — non zero, non due. Attorno al trigger puoi invece dichiarare quanti binding di input e output vuoi: sono connessioni verso servizi esterni (Storage, Service Bus, Cosmos DB, SendGrid…) che il runtime risolve automaticamente, iniettando i dati in ingresso e scrivendo quelli in uscita al termine.

La configurazione avviene in due modi equivalenti a seconda del linguaggio:

  • Attributi in-process/isolated (C#, Java) — es. [QueueTrigger], [BlobOutput] applicati ai parametri del metodo.
  • function.json (scripting: JavaScript, Python model v1, PowerShell) — un array bindings dove ogni voce ha type, direction (in/out) e name. Nel modello Python v2 e nel worker isolato .NET si usano invece i decoratori/attributi.

Il vantaggio dei binding dichiarativi è togliere codice di plumbing: niente creazione manuale di QueueClient o gestione della connessione. Lo svantaggio è la minor granularità — se ti serve controllo fine (paginazione, transazioni, retry personalizzati, operazioni condizionali) chiami direttamente l’SDK dentro la funzione. Regola pratica da ASSOCIATE: binding per I/O semplice e dichiarativo, SDK quando la logica di accesso al dato è essa stessa parte del problema.

Panoramica dei trigger chiave

  • HTTP trigger — invocazione via richiesta HTTP, con authLevel (anonymous/function/admin). Base per API e webhook.
  • Timer trigger — esecuzione schedulata tramite espressione NCRONTAB a 6 campi {secondo} {minuto} {ora} {giorno} {mese} {giorno-settimana}. Attenzione: parte dai secondi, a differenza del cron Linux. 0 */5 * * * * = ogni 5 minuti; 0 30 9 * * 1-5 = 9:30 nei giorni feriali.
  • Queue Storage trigger — reagisce ai messaggi di una coda; il runtime gestisce il polling, il lease e lo spostamento in poison queue dopo maxDequeueCount tentativi falliti.
  • Blob Storage trigger — si attiva su nuovi blob. Sul piano Consumption usa un meccanismo di polling/scan del log, non è in tempo reale né garantito per volumi elevati (vedi trappola sotto).
  • Service Bus trigger — code e topic/subscription, con semantica transazionale, sessioni e dead-letter: la scelta enterprise quando servono ordinamento, FIFO o at-least-once affidabile.
  • Event Grid trigger — consumo push di eventi discreti (creazione blob, eventi di risorse Azure, eventi custom): bassa latenza, retry con backoff, ideale per architetture event-driven reattive.
  • Event Hubs trigger — ingestione ad alto throughput di stream telemetrici/log, processati in batch con checkpoint tramite storage.

Binding di input e output

Un pattern tipico: HTTP trigger + Cosmos DB input binding (leggi un documento by-id) + Queue output binding (accoda un lavoro). Tre servizi orchestrati senza istanziare un solo client. Un output binding può anche restituire collezioni (ICollector<T> / IAsyncCollector<T>) per emettere più messaggi in una singola esecuzione.

Blob trigger vs Event Grid: la scelta architetturale

Il punto più insidioso riguarda il blob trigger. Sul piano Consumption il rilevamento si basa sulla scansione periodica dei log dello storage: la latenza può arrivare a diversi minuti e, oltre le 10.000 blob, alcuni eventi possono sfuggire. Se lo scenario richiede reattività quasi immediata e affidabilità, la risposta corretta è usare Event Grid come sorgente: si sottoscrive l’evento Microsoft.Storage.BlobCreated e si usa il tipo di trigger Blob con source: EventGrid (o direttamente un Event Grid trigger). Event Grid fa push con retry integrati, eliminando il polling.

Trappole tipiche d’esame

  • Serve elaborare i blob appena caricati, in tempo reale e affidabile, su piano Consumption → non affidarti al blob trigger basato su polling: sottoscrivi l’evento BlobCreated via Event Grid (blob trigger con source: EventGrid).
  • Devi eseguire una funzione ogni notte alle 2:00Timer trigger con NCRONTAB 0 0 2 * * *; ricorda i 6 campi con i secondi in testa, non i 5 del cron classico.
  • Vuoi che una funzione legga da una coda e scriva su Cosmos DB senza SDK → un solo queue trigger più un output binding Cosmos DB; non aggiungere un secondo trigger.
  • Messaggi che richiedono FIFO, sessioni o dead-letter garantitoService Bus, non Queue Storage.
  • Un messaggio di coda fallisce ripetutamente → dopo maxDequeueCount finisce nella poison queue; non resta in loop infinito.