Modello di coda di priorità

Classificare in ordine di priorità le richieste inviate ai servizi in modo che un carico di lavoro elabori le richieste con priorità alta più rapidamente rispetto a quelle con priorità inferiore. Questo approccio usa i messaggi inviati a una o più code ed è utile per le applicazioni che forniscono diversi livelli di servizio o contratti di servizio a diversi tipi di richiesta o clienti.

Contesto e problema

I carichi di lavoro potrebbero dover gestire ed elaborare le attività con diversi livelli di importanza e urgenza. Alcune attività richiedono un'attenzione immediata, mentre altre possono attendere. Il mancato svolgimento delle attività ad alta priorità può compromettere l'esperienza utente e comportare una violazione degli SLA.

Per gestire le attività in modo efficiente in base alla priorità, i carichi di lavoro necessitano di un meccanismo per elaborare ed eseguire le attività di conseguenza. Per impostazione predefinita, la maggior parte dei carichi di lavoro elabora le attività nell'ordine in cui arrivano, utilizzando una struttura di coda FIFO (First-In, First-Out). Questo approccio non tiene conto dell'importanza variabile delle attività.

Soluzione

Le code con priorità consentono ai carichi di lavoro di elaborare le attività in base alla priorità anziché esclusivamente in base all'ordine di arrivo. L'applicazione o il producer che invia una richiesta assegna un valore di priorità al messaggio e i consumer elaborano i messaggi in base alla priorità. Il modello Priority Queue soddisfa i requisiti seguenti:

  • Gestisce le attività di varia urgenza e importanza: Si dispone di attività con diversi livelli di urgenza e importanza ed è necessario assicurarsi di elaborare attività più critiche prima di quelle meno critiche.

  • Gestisce contratti di servizio diversi: Si offrono contratti di servizio diversi a clienti diversi ed è necessario garantire che i clienti con priorità elevata ricevano prestazioni e disponibilità migliori.

  • Supporta diverse esigenze di gestione dei carichi di lavoro: È disponibile un carico di lavoro che deve gestire immediatamente determinate attività, mentre le attività meno urgenti possono attendere.

Esistono due approcci principali per implementare il modello di coda prioritaria:

  • Coda singola: A ogni messaggio viene assegnato un valore di priorità e tutti i messaggi usano la stessa coda.

  • Code multiple: A ogni messaggio viene assegnato un valore di priorità e i messaggi con priorità diversa usano code separate.

Coda singola

In un approccio a una singola coda, l'applicazione assegna una priorità a ogni messaggio e invia tutti i messaggi a una singola coda. La coda ordina i messaggi in base alla priorità, assicurandosi che i consumer eselaborino i messaggi con priorità più alta prima di quelli con priorità più bassa.

Diagramma che illustra un meccanismo di accodamento che supporta la definizione delle priorità dei messaggi.

Più code

Più code separano i messaggi in base alla priorità. L'applicazione assegna una priorità a ogni messaggio e indirizza il messaggio alla coda che corrisponde alla priorità, in cui i consumer elaborano i messaggi. Una soluzione con più code può utilizzare un singolo gruppo di consumatori oppure più gruppi di consumatori.

Pool di un singolo consumatore

In una configurazione con un unico pool, tutte le code condividono lo stesso pool di consumatori. I consumer elaborano prima i messaggi dalla coda a priorità più alta e processano i messaggi dalle code a priorità inferiore solo quando non ci sono più messaggi di alta priorità. Di conseguenza, i singoli pool di consumer elaborano sempre messaggi con priorità più alta prima di quelli con priorità più bassa. Questa configurazione può causare un ritardo continuo dei messaggi con priorità inferiore e potenzialmente non elaborati.

Diagramma che illustra l'uso di un singolo pool di consumer per tutte le priorità.

Usare un singolo pool di consumer per i motivi seguenti:

  • Gestione semplice. Usare un singolo pool di consumer quando la semplicità di configurazione e manutenzione è una priorità. Un singolo pool riduce la complessità di configurazione e monitoraggio.

  • Esigenze di elaborazione unificata. Utilizzare un singolo pool di consumer quando le attività in ingresso sono simili per tipo.

Più pool di consumatori

In un pool con più consumer, ogni coda ha un pool di consumer dedicato. Le code con priorità più alta usano più consumer o livelli di prestazioni più elevati per elaborare i messaggi più velocemente rispetto alle code con priorità inferiore.

Diagramma che illustra l'uso di pool di consumer separati per ogni priorità.

Usare più pool di consumer per i motivi seguenti:

  • Requisiti di prestazioni rigorosi. Usare più pool di consumer quando attività con priorità diverse hanno requisiti prestazionali rigorosi che devono essere soddisfatti separatamente.

  • Esigenze di affidabilità elevata. Usare più pool di consumer per le applicazioni quando l'affidabilità e l'isolamento dei guasti sono fondamentali e i problemi in una coda non devono ripercuotersi sulle altre code.

  • Applicazioni complesse. Usare più pool di consumer per applicazioni complesse in cui diverse attività richiedono caratteristiche di elaborazione e garanzie di prestazioni diverse.

Problemi e considerazioni

Quando si decide come implementare questo modello, tenere presente quanto segue:

Raccomandazioni generali

  • Definire chiaramente le priorità. Stabilire livelli di priorità distinti e chiari rilevanti per la soluzione. Ad esempio, è possibile definire messaggi ad alta priorità come quelli che richiedono l'elaborazione entro 10 secondi. Identificare i requisiti dei consumer per la gestione degli elementi con priorità elevata e allocare le risorse necessarie di conseguenza.

  • Regolare dinamicamente i pool di consumer. Ridimensiona i pool di consumer in base alla lunghezza della coda che gestiscono.

  • Monitora lo stato della coda. Tenere traccia della profondità della coda, latenza di elaborazione, numero di recapito e velocità effettiva, in modo da poter rilevare backlog e rallentamenti prima che influiscano sul lavoro.

  • Usare code di messaggi non recapitabili. Spostare i messaggi poison in una coda dei messaggi non recapitabili dopo un numero configurabile di tentativi di recapito, in modo che un singolo messaggio difettoso non blocchi il percorso prioritario.

  • Classificare in ordine di priorità i livelli di servizio. Implementare code con priorità per soddisfare le esigenze aziendali che richiedono disponibilità o prestazioni prioritarie. Ad esempio, i clienti con priorità elevata possono ricevere un livello di servizio superiore in modo da ottenere prestazioni e disponibilità migliori.

  • Prendere in considerazione l'elaborazione con priorità bassa. Decidere se tutti gli elementi con priorità alta devono essere elaborati prima di qualsiasi elemento con priorità inferiore. Se possibile, aumentare dinamicamente la priorità dei messaggi precedenti per garantire che i messaggi con priorità bassa vengano elaborati.

  • Ottimizzare e ridurre al minimo i costi. Elaborare immediatamente le attività critiche con i consumatori disponibili. Pianificare attività in background meno critiche durante gli orari meno occupati.

    Se utilizzi una singola coda, riduci i costi diminuendo il numero di consumer. I messaggi con priorità alta elaborano prima ma probabilmente più lentamente, mentre i messaggi con priorità più bassa potrebbero riscontrare ritardi più lunghi.

  • Proteggere i processori dai picchi di domanda. Se la frequenza di arrivo del produttore può superare la capacità di elaborazione dei consumatori, combinare questo schema con il modello di livellamento del carico basato su coda. Questo approccio memorizza nel buffer i picchi di traffico e impedisce l'overload delle risorse di elaborazione downstream.

Raccomandazioni per più code

  • Monitorare le velocità di elaborazione. Per garantire che i messaggi vengano elaborati a velocità previste, monitorare continuamente la velocità di elaborazione delle code con priorità alta e bassa.

  • Implementare la precedenza e la sospensione. Se si utilizzano più code con un singolo pool di consumer, implementare un algoritmo che garantisca che le code ad alta priorità vengano sempre servite prima di quelle a priorità inferiore.

  • Prendere in considerazione i costi della coda. Tenete presente i costi economici associati alla verifica e all'elaborazione delle code. Alcuni servizi di coda addebitano costi per l'invio, il recupero e l'interrogazione dei messaggi. Queste tariffe possono aumentare con il numero di code.

Quando usare questo modello

Usare questo modello quando:

  • È necessario soddisfare diversi obiettivi di latenza o di servizio per diverse classi di lavoro, ad esempio premium e richieste standard dei clienti.

  • Il lavoro arriva a ondate e occorre proteggere le operazioni critiche elaborando prima i messaggi ad alta priorità, rimandando le attività a priorità inferiore.

Questo modello potrebbe non essere adatto quando:

  • Tutti gli elementi di lavoro hanno un'importanza aziendale simile e l'elaborazione FIFO rigorosa è più importante rispetto alla pianificazione basata sulla priorità.

  • Le attività hanno dipendenze di ordinamento forte tra i livelli di priorità e il riordinamento del lavoro per priorità può causare risultati incoerenti o richiedere una logica di coordinamento complessa.

Progettazione del carico di lavoro

Valutare come usare il modello di coda con priorità nella progettazione di un carico di lavoro per rispondere agli obiettivi e ai principi illustrati nei pilastri del framework Azure Well-Architected. La tabella seguente fornisce indicazioni su come questo modello supporta gli obiettivi di ogni pilastro.

Pilastro Come questo modello supporta gli obiettivi di pilastro
Le decisioni di progettazione dell'affidabilità consentono al carico di lavoro di diventare resilienti a malfunzionamenti e di assicurarsi che venga ripristinato in uno stato completamente funzionante dopo che si verifica un errore. La separazione degli elementi in base alla priorità aziendale consente di concentrare le attività di affidabilità sul lavoro più critico.

- Flussi critici RE:02
l'efficienza delle prestazioni consente al carico di lavoro soddisfare in modo efficiente le richieste tramite ottimizzazioni di ridimensionamento, dati e codice. La separazione degli elementi in base alla priorità aziendale consente di concentrare le attività di prestazioni sul lavoro più sensibile al tempo.

- Flussi critici PE:09

Se questo modello introduce compromessi all'interno di un pilastro, considerarli contro gli obiettivi degli altri pilastri.

Example

L'esempio del pattern Coda con priorità su GitHub mostra un'implementazione del pattern Coda con priorità che usa argomenti e sottoscrizioni di bus di servizio di Azure. L'esempio distribuisce un account di archiviazione sicuro, una risorsa di Application Insights per il monitoraggio e uno spazio dei nomi bus di servizio per abilitare la comunicazione tra le funzioni mittente e consumer.

La distribuzione comprende tre app funzione: un'app mittente e due app consumer. Le applicazioni consumer utilizzano un diverso numero massimo di istanze per simulare l’assegnazione di priorità ai messaggi. La funcPriorityQueueConsumerHigh funzione può aumentare fino a 200 istanze, mentre la funcPriorityQueueConsumerLow funzione è limitata a 40 istanze. Tutte le app per le funzioni usano il piano a consumo Flex e sono connesse ad Application Insights per la diagnostica e il monitoraggio.

Le assegnazioni di ruolo concedono l'accesso sicuro alle bus di servizio e all'archiviazione usando le identità gestite. Tutte le app per le funzioni condividono lo stesso account di archiviazione e la stessa risorsa di Application Insights. Questa configurazione centralizza l'osservabilità e la registrazione.

Il diagramma seguente illustra l'architettura della coda con priorità:

Diagramma che mostra come implementare una coda di priorità usando bus di servizio.

Nel diagramma precedente:

  1. Applicazione (produttore). L'applicazione PriorityQueueSender crea messaggi, assegna una proprietà dell'applicazione personalizzata denominata Priority a ogni messaggio e imposta il Priority valore su High o Low.

  2. Broker di messaggi e argomento. Il broker di messaggi bus di servizio invia messaggi a un singolo argomento bus di servizio denominato messages. bus di servizio usa filtri SQL per instradare ogni messaggio alla sottoscrizione con priorità alta o con priorità bassa, in base al relativo Priority valore.

  3. Più pool di consumatori. I pool di consumer PriorityQueueConsumerHigh e PriorityQueueConsumerLow rispondono ai messaggi provenienti dalle sottoscrizioni a priorità alta o bassa utilizzando i trigger di bus di servizio di Funzioni di Azure.

Ruolo nell'esempio Servizio Azure nell'esempio Nome nell'esempio
Applicazione (produttore) App Funzioni di Azure PriorityQueueSender
Broker di messaggi Bus di servizio di Azure <spazio dei nomi del bus di servizio>
Argomento del messaggio Argomento del bus di servizio di Azure messages
Sottoscrizioni di messaggi sottoscrizioni bus di servizio di Azure highPriority
lowPriority
Consumatori App Funzioni di Azure PriorityQueueConsumerHigh
PriorityQueueConsumerLow

Passaggi successivi

I modelli seguenti possono essere utili quando si implementa questo modello:

  • Modello di livellamento del carico basato su code: usare una coda come buffer tra la ricezione delle richieste e la relativa elaborazione. Usalo con il pattern Priority Queue quando hai bisogno sia della protezione dai picchi sia della gestione differenziata.

  • Modello consumer concorrenti: implementare più consumer in ascolto della stessa coda e delle stesse attività di elaborazione in parallelo per aumentare la velocità effettiva. Ogni messaggio viene elaborato da un solo consumer.

  • Pattern di limitazione: Implementare la limitazione utilizzando code per gestire la frequenza delle richieste. Usare la messaggistica con priorità per classificare in ordine di priorità le richieste da applicazioni critiche o clienti di alto valore rispetto a quelle meno importanti.