Bearbeiten

Muster „Prioritätswarteschlange“

Azure-Servicebus

Priorisieren Sie an Dienste gesendete Anforderungen, sodass Anforderungen mit einer höheren Priorität schneller empfangen und verarbeitet werden als Anforderungen mit einer niedrigeren Priorität. Dieses Muster erweist sich bei Anwendungen als nützlich, die einzelnen Clients unterschiedliche Servicelevelgarantien bieten.

Kontext und Problem

Anwendungen können bestimmte Aufgaben an andere Dienste delegieren, beispielsweise für die Ausführung der Hintergrundverarbeitung oder Integration in andere Anwendungen oder Dienste. In der Cloud werden mit Nachrichtenwarteschlangen üblicherweise Aufgaben an die Hintergrundverarbeitung delegiert. Die Reihenfolge, in der Anforderungen von einem Dienst empfangen werden, ist in vielen Fällen unerheblich. In manchen Fällen ist es jedoch notwendig, bestimmte Anforderungen zu priorisieren. Diese Anforderungen sollten vor Anforderungen mit niedrigerer Priorität, die zuvor von der Anwendung gesendet wurden, verarbeitet werden.

Lösung

Eine Warteschlange folgt in der Regel dem FIFO-Prinzip (First In, First Out), und Consumer erhalten Nachrichten normalerweise in der gleichen Reihenfolge, in der sie in der Warteschlange bereitgestellt wurden. Einige Nachrichtenwarteschlangen unterstützen jedoch priorisierte Nachrichten. Die Anwendung, die eine Nachricht veröffentlicht, kann eine Priorität zuweisen. Die Nachrichten in der Warteschlange werden automatisch neu sortiert, sodass Nachrichten mit höherer Priorität vor Nachrichten mit niedriger Priorität empfangen werden. In diesem Diagramm wird der Prozess dargestellt:

Diagramm eines Warteschlangenmechanismus mit Unterstützung für die Priorisierung von Nachrichten.

Hinweis

Die meisten Implementierungen für Nachrichtenwarteschlangen unterstützen mehrere Consumer. (Siehe Muster konkurrierender Consumer.) Die Anzahl der Consumer-Prozesse kann je nach Bedarf hoch- und herunterskaliert werden.

In Systemen, die keine prioritätsbasierten Nachrichtenwarteschlangen unterstützen, kann alternativ für jede Priorität eine eigene Warteschlange verwaltet werden. Die Anwendung ist für die Bereitstellung von Nachrichten in der entsprechenden Warteschlange zuständig. Jede Warteschlange kann über einen separaten Pool von Consumern verfügen. Für Warteschlangen mit höherer Priorität kann ein größerer Consumerpool auf schnellerer Hardware ausgeführt werden als für Warteschlangen mit niedrigerer Priorität. Dieses Diagramm veranschaulicht die Verwendung separater Nachrichtenwarteschlangen für jede Priorität:

Diagramm zur Veranschaulichung der Verwendung separater Nachrichtenwarteschlangen für jede Priorität.

Eine Variante dieser Strategie besteht darin, einen einzigen Consumerpool zu verwalten, der zuerst in Warteschlangen mit hoher Priorität nach Nachrichten sucht und erst dann damit beginnt, Nachrichten aus Warteschlangen mit niedrigerer Priorität abzurufen. Es gibt einige semantische Unterschiede zwischen einer Lösung, die einen einzigen Pool von Consumerprozessen verwendet (entweder mit einer einzigen Warteschlange, die Nachrichten mit unterschiedlichen Prioritäten unterstützt, oder mit mehreren Warteschlangen, die jeweils Nachrichten mit einer einzigen Priorität verarbeiten), und einer Lösung, die mehrere Warteschlangen mit einem separaten Pool für jede Warteschlange verwendet.

Bei dem Modell mit einem einzigen Pool werden Nachrichten mit höherer Priorität stets vor Nachrichten mit niedrigerer Priorität empfangen und verarbeitet. Theoretisch könnten Nachrichten mit einer sehr niedrigen Priorität kontinuierlich verdrängt und niemals verarbeitet werden. Bei dem Modell mit mehreren Pools werden Nachrichten mit niedrigerer Priorität immer verarbeitet, nur nicht so schnell wie Nachrichten mit höherer Priorität (abhängig von der relativen Größe der Pools und den für diese Pools zur Verfügung stehenden Ressourcen).

Die Verwendung eines Prioritätswarteschlangenmechanismus kann folgende Vorteile bieten:

  • Durch diesen Mechanismus wird es Anwendungen ermöglicht, Geschäftsanforderungen zu erfüllen, die eine Priorisierung der Verfügbarkeit oder Leistung erfordern, wie durch das Anbieten unterschiedlicher Servicelevels für unterschiedliche Kundengruppen.

  • Der Mechanismus kann zur Minimierung der Betriebskosten beitragen. Wenn Sie das Modell mit einer einzigen Warteschlange verwenden, können Sie die Anzahl der Consumer bei Bedarf reduzieren. Nachrichten mit hoher Priorität werden nach wie vor zuerst verarbeitet (wenn auch möglicherweise langsamer), und Nachrichten mit niedrigerer Priorität werden möglicherweise länger verzögert. Wenn Sie das Modell mit mehreren Warteschlangen mit separaten Consumerpools für jede Warteschlange einsetzen, können Sie den Consumerpool für Warteschlangen mit niedriger Priorität verkleinern. Sie können die Verarbeitung für einige Warteschlangen mit sehr niedriger Priorität sogar anhalten, indem Sie alle Consumer stoppen, die auf Nachrichten aus diesen Warteschlangen lauschen.

  • Durch das Modell mit mehreren Nachrichtenwarteschlangen können Anwendungsleistung und Skalierbarkeit maximiert werden, indem Nachrichten gemäß den Verarbeitungsanforderungen partitioniert werden. Beispielsweise können Sie kritische Aufgaben priorisieren, sodass sie von Empfängern behandelt werden, die sofort ausgeführt werden, und weniger wichtige Hintergrundaufgaben von Empfängern verarbeitet werden, die zu Zeiten mit weniger Auslastung ausgeführt werden sollen.

Überlegungen

Beachten Sie die folgenden Punkte bei Ihrer Entscheidung, wie dieses Muster implementiert werden soll:

  • Definieren Sie die Prioritäten im Kontext der Lösung. Beispielsweise kann eine Nachricht mit hoher Priorität als Nachricht definiert werden, die innerhalb von 10 Sekunden verarbeitet werden soll. Ermitteln Sie die Anforderungen für die Bearbeitung von Elementen mit hoher Priorität und Ressourcen, die zur Erfüllung Ihrer Kriterien zugewiesen werden müssen.

  • Legen Sie fest, ob alle Elemente mit hoher Priorität vor sämtlichen Elementen mit niedrigerer Priorität verarbeitet werden müssen. Wenn die Nachrichten von einem einzigen Consumerpool verarbeitet werden, müssen Sie einen Mechanismus bereitstellen, der eine Aufgabe zur Bearbeitung einer Nachricht mit niedriger Priorität verdrängen und anhalten kann, wenn eine Nachricht mit höherer Priorität die Warteschlange erreicht.

  • Wenn Sie bei einem Modell mit mehreren Warteschlangen einen einzigen Pool mit Consumerprozessen einsetzen, der auf alle Warteschlangen lauscht statt auf einen dedizierten Consumerpool für jede Warteschlange, muss auf den Consumer ein Algorithmus angewendet werden, der sicherstellt, dass er Nachrichten aus Warteschlangen mit höherer Priorität immer vor Nachrichten aus Warteschlangen mit niedrigerer Priorität verarbeitet.

  • Überwachen Sie die Verarbeitungsgeschwindigkeit von Warteschlangen mit hoher und niedriger Priorität, um sicherzustellen, dass Nachrichten in diesen Warteschlangen mit der erwarteten Geschwindigkeit verarbeitet werden.

  • Wenn Sie sicherstellen müssen, dass Nachrichten mit niedriger Priorität verarbeitet werden, implementieren Sie das Modell mit mehreren Warteschlangen mit mehreren Consumerpools. Alternativ können Sie bei einer Warteschlange mit Unterstützung für Nachrichtenpriorisierung die Priorität einer in der Warteschlange gereihten Nachricht mit fortlaufender Dauer dynamisch erhöhen. Um dieses Modell verwenden zu können, muss jedoch gegeben sein, dass dieses Feature von der Nachrichtenwarteschlange bereitgestellt wird.

  • Die Strategie zur Verwendung separater Warteschlangen basierend auf der Nachrichtenpriorität wird für Systeme mit einigen klar definierten Prioritäten empfohlen.

  • Das System kann Nachrichtenprioritäten logisch bestimmen. Anstatt beispielsweise explizite Nachrichten mit hoher und niedriger Priorität zu haben, könnten Sie Nachrichten als "zahlender Kunde" oder "nicht zahlender Kunde" festlegen. Ihr System könnte dann mehr Ressourcen für die Verarbeitung von Nachrichten zahlender Kunden zuweisen.

  • Mit der Überprüfung einer Warteschlange auf eine Nachricht können finanzielle Kosten und Verarbeitungskosten verbunden sein. Einige kommerzielle Messagingsysteme erheben beispielsweise eine geringe Gebühr, wenn eine Nachricht bereitgestellt oder abgerufen wird und Nachrichten in einer Warteschlange abgefragt werden. Bei der Prüfung mehrerer Warteschlangen fallen diese Kosten höher aus.

  • Sie können die Größe eines Consumerpools basierend auf der Länge der Warteschlange, die der Pool bedient, dynamisch anpassen. Weitere Informationen finden Sie im Leitfaden für die automatische Skalierung.

Verwendung dieses Musters

Dieses Muster ist in Szenarien wie den folgenden nützlich:

  • Das System muss mehrere Aufgaben mit unterschiedlichen Prioritäten verarbeiten.

  • Unterschiedliche Benutzer oder Mandanten sollten mit unterschiedlichen Prioritäten versehen werden.

Workloadentwurf

Ein Architekt sollte evaluieren, wie das Prioritäts-Warteschlangen-Muster im Design seiner Workloads verwendet werden kann, um die Ziele und Prinzipien zu erreichen, die in den Azure Well-Architected Framework-Säulen behandelt werden. Zum Beispiel:

Säule So unterstützt dieses Muster die Säulenziele
Zuverlässigkeitsdesignentscheidungen tragen dazu bei, dass Ihre Workload ausfallsicher wird und dass sie nach einem Ausfall wieder in einen voll funktionsfähigen Zustand zurückkehrt. Durch die Trennung von Aufgaben auf der Grundlage der geschäftlichen Priorität können Sie Ihre Bemühungen um die Zuverlässigkeit auf die wichtigsten Aufgaben konzentrieren.

- RE:02 Kritische Flows
- RE:07 Hintergrundaufträge
Die Leistungseffizienz hilft Ihrer Workload, Anforderungen effizient durch Optimierungen in Skalierung, Daten und Code zu erfüllen. Die Trennung von Aufgaben nach geschäftlicher Priorität ermöglicht es Ihnen, die Leistungsbemühungen auf die zeitkritischsten Aufgaben zu konzentrieren.

- PE:09 Kritische Flows

Berücksichtigen Sie wie bei jeder Designentscheidung alle Kompromisse im Hinblick auf die Ziele der anderen Säulen, die mit diesem Muster eingeführt werden könnten.

Beispiel

Azure bietet keinen Queuing-Mechanismus, der die automatische Priorisierung von Nachrichten durch Sortierung nativ unterstützt. Azure bietet jedoch Azure Service Bus-Themen, Service Bus-Abonnements, die einen Queuing-Mechanismus mit Nachrichtenfilterung sowie einem großen Spektrum an flexiblen Funktionen unterstützen. Somit ist Azure die ideale Lösung für den Einsatz in den meisten Implementierungen von Prioritätswarteschlangen.

Eine Azure-Lösung kann ein Service Bus-Thema implementieren, für das eine Anwendung genau wie bei einer Warteschlange Nachrichten bereitstellen kann. Nachrichten können Metadaten in Form von anwendungs- und benutzerdefinierten Eigenschaften enthalten. Sie können dem Thema Service Bus-Abonnements zuordnen, die Nachrichten anhand ihrer Eigenschaften filtern können. Wenn eine Anwendung eine Nachricht an ein Thema sendet, wird die Nachricht an das entsprechende Abonnement weitergeleitet, wo ein Consumer die Nachricht lesen kann. Consumerprozesse können Nachrichten aus einem Abonnement abrufen, indem sie dieselbe Semantik wie für eine Nachrichtenwarteschlange verwenden. (Ein Abonnement ist eine logische Warteschlange.) Dieses Diagramm zeigt, wie Sie eine Prioritätswarteschlange mithilfe von Service Bus-Themen und -Abonnements implementieren:

Diagramm zum Implementieren einer Prioritätswarteschlange mithilfe von Service Bus-Themen und -Abonnements.

In der Abbildung oben erstellt die Anwendung mehrere Nachrichten und weist jeder Nachricht eine benutzerdefinierte Eigenschaft namens Priority. Priority hat den Wert High oder Low. Diese Nachrichten werden von der Anwendung für ein Thema bereitgestellt. Dem Thema sind zwei Abonnements zugeordnet, die beide anhand der Eigenschaft Priority Nachrichten filtern. Ein Abonnement akzeptiert Nachrichten, deren Eigenschaft Priority auf High festgelegt ist. Das andere Abonnement akzeptiert Nachrichten, deren Eigenschaft Priority auf Low festgelegt ist. Ein Consumerpool liest Nachrichten aus jedem Abonnement. Das Abonnement mit hoher Priorität weist einen größeren Pool auf. Diese Consumer können eventuell auf leistungsstärkeren Computern mit mehr verfügbaren Ressourcen ausgeführt werden als die Consumer im Pool mit niedriger Priorität.

Die Benennung von Nachrichten mit hoher und niedriger Priorität stellt in diesem Beispiel keine Besonderheit dar. Dabei handelt es sich lediglich um Bezeichnungen, die in jeder Nachricht als Eigenschaften angegeben werden. Diese Bezeichnungen werden verwendet, um Nachrichten zu einem bestimmten Abonnement zu leiten. Falls zusätzliche Prioritäten erforderlich sind, ist es relativ einfach, weitere Abonnements und Pools von Consumerprozessen für die Verarbeitung dieser Prioritäten zu erstellen.

Die PriorityQueue-Lösung auf GitHub basiert auf diesem Ansatz. Diese Lösung enthält Azure Functions-Projekte mit den Namen PriorityQueueConsumerHigh und PriorityQueueConsumerLow. Diese Projekte zu Azure-Funktionen werden über Trigger und Bindungen in Service Bus integriert. Sie stellen eine Verbindung mit verschiedenen Abonnements her, die in ServiceBusTrigger definiert sind und reagieren auf eingehende Nachrichten.

public static class PriorityQueueConsumerHighFn
{
    [FunctionName("HighPriorityQueueConsumerFunction")]
    public static void Run(
      [ServiceBusTrigger("messages", "highPriority", Connection = "ServiceBusConnection")]string highPriorityMessage,
      ILogger log)
    {
        log.LogInformation($"C# ServiceBus topic trigger function processed message: {highPriorityMessage}");
    }
}

Als Administrator können Sie konfigurieren, über wie viele Instanzen die Funktionen auf Azure App Service hochskaliert werden können. Dazu konfigurieren Sie die Option Erzwingen von Scale Out Limit über Azure-Portal, indem Sie ein maximales Horizontalskalierungslimit für jede Funktion festlegen. Sie benötigen in der Regel mehr Instanzen mit der Funktion PriorityQueueConsumerHigh als mit der Funktion PriorityQueueConsumerLow. Diese Konfiguration stellt sicher, dass Nachrichten mit hoher Priorität schneller aus der Warteschlange gelesen werden als Nachrichten mit niedriger Priorität.

Ein anderes Projekt, PriorityQueueSender, enthält eine zeitgesteuerte Azure-Funktion, die so konfiguriert ist, dass sie alle 30 Sekunden ausgeführt wird. Diese Funktion lässt sich über eine Ausgabebindung in Service Bus integrieren und sendet Batches mit Nachrichten mit niedriger und hoher Priorität an ein Objekt IAsyncCollector. Wenn die Funktion Nachrichten zu dem Thema bereitstellt, das den von den Funktionen PriorityQueueConsumerHigh und PriorityQueueConsumerLow verwendeten Abonnements zugeordnet ist, legt sie die Priorität mithilfe der benutzerdefinierten Eigenschaft Priority wie folgt fest:

public static class PriorityQueueSenderFn
{
    [FunctionName("PriorityQueueSenderFunction")]
    public static async Task Run(
        [TimerTrigger("0,30 * * * * *")] TimerInfo myTimer,
        [ServiceBus("messages", Connection = "ServiceBusConnection")] IAsyncCollector<ServiceBusMessage> collector)
    {
        for (int i = 0; i < 10; i++)
        {
            var messageId = Guid.NewGuid().ToString();
            var lpMessage = new ServiceBusMessage() { MessageId = messageId };
            lpMessage.ApplicationProperties["Priority"] = Priority.Low;
            lpMessage.Body = BinaryData.FromString($"Low priority message with Id: {messageId}");
            await collector.AddAsync(lpMessage);

            messageId = Guid.NewGuid().ToString();
            var hpMessage = new ServiceBusMessage() { MessageId = messageId };
            hpMessage.ApplicationProperties["Priority"] = Priority.High;
            hpMessage.Body = BinaryData.FromString($"High priority message with Id: {messageId}");
            await collector.AddAsync(hpMessage);
        }
    }
}

Nächste Schritte

Die folgenden Ressourcen können für Sie beim Implementieren dieses Musters hilfreich sein:

  • Ein Beispiel zur Demonstration dieses Musters auf GitHub.

  • Einführung in asynchrone Nachrichten. Ein Consumerdienst, der eine Anforderung verarbeitet, muss möglicherweise eine Antwort an die Instanz der Anwendung senden, die die Anforderung bereitgestellt hat. Dieser Artikel stellt Informationen zu den Strategien bereit, die Sie zur Implementierung von Anforderung-/Antwort-Nachrichten verwenden können.

  • Leitfaden für die automatische Skalierung. Sie können in einigen Fällen die Größe des Pools von Consumerprozessen, die eine Warteschlange verarbeiten, abhängig von der Länge der Warteschlange skalieren. Diese Strategie kann Ihnen dabei helfen, die Leistung insbesondere bei Pools zu verbessern, die Nachrichten mit hoher Priorität verarbeiten.

Die folgenden Muster können für Sie beim Implementieren hilfreich sein:

  • Muster „Konkurrierende Consumer“: Um den Durchsatz der Warteschlangen zu erhöhen, können Sie mehrere Consumer auf die gleiche Warteschlange lauschen und die Aufgaben parallel verarbeiten lassen. Diese Consumer konkurrieren um Nachrichten, doch nur ein Consumer sollte in der Lage sein, die einzelnen Nachrichten zu verarbeiten. Dieser Artikel bietet weitere Informationen über die Vor- und Nachteile, die sich bei der Implementierung dieser Vorgehensweise ergeben.

  • Muster „Drosselung“: Sie können Drosselungen über Warteschlangen implementieren. Sie können über Prioritätsnachrichten sicherstellen, dass Anforderungen von kritischen Anwendungen oder Anwendungen, die von wichtigen Kunden ausgeführt werden, Vorrang gegenüber Anforderungen von weniger wichtigen Anwendungen haben.