Sticky Session Load Balancing – MQTT-Broker-Clustering Teil 2

Im letzten Beitrag: Load Balancing – MQTT-Broker-Clustering Teil 1haben wir MQTT Load Balancing im Allgemeinen eingeführt: Load Balancing kann entweder auf der Transportschicht oder der Anwendungsschicht angewendet werden. Jetzt ist es an der Zeit, in den Lastausgleich auf Anwendungsebene einzutauchen, den interessantesten Teil: Sticky-Session.

Dieser Beitrag besteht aus 2 Teilen, der erste Teil stellt die MQTT-Sitzungen und die Herausforderungen bei der Übergabe von Sitzungen in einem verteilten MQTT-Broker-Cluster vor; Der zweite Teil besteht darin, sich die Hände schmutzig zu machen, indem wir an versorgen HAProxy 2.4 Loadbalancer davor EMQX 4.3 Cluster, um den Sticky-Session-Load-Balancing voll auszunutzen.

MQTT-Sitzung

Um kontinuierlich Nachrichten zu erhalten, abonnieren sich MQTT-Clients in der Regel MQTT-Broker mit einer langlebigen Verbindung. Es ist nicht ungewöhnlich, dass die Verbindung aufgrund von Netzwerkproblemen oder Wartungsgründen der Client-Software für eine Weile unterbrochen wird, aber die Clients möchten häufig Nachrichten erhalten, die während der Zeit veröffentlicht wurden, als die Verbindung unterbrochen wurde.

Aus diesem Grund sollte der MQTT-Broker, der den Client bedient, eine Sitzung für den Client aufrechterhalten (auf Anfrage des Clients, indem das „Clean-Session“-Flag auf „false“ gesetzt wird). Daher werden die Themen, die der Abonnent derzeit abonniert hat, und Nachrichten (von QoS1 und 2), die an diese Themen usw. geliefert werden, vom Broker aufbewahrt, selbst wenn der Client getrennt ist.

Wenn sich ein Client mit dauerhafter Sitzung wieder verbindet, sollte er die Themen nicht erneut abonnieren müssen, und der Broker sollte alle anstehenden Nachrichten an den Client senden.

Darüber haben wir bereits einen Artikel geschrieben MQTT-Sitzungenes ist eine großartige Lektüre, wenn Sie an weiteren technischen Details zu MQTT-Sitzungen interessiert sind.

Sitzungsübernahme

Die Dinge werden etwas komplizierter, wenn MQTT-Broker einen Cluster bilden. Aus Sicht des Kunden gibt es mehr als einen Broker, mit dem man sich verbinden kann, und es ist schwer zu wissen, mit welchem ​​Broker man sich am besten verbindet. Wir brauchen eine weitere kritische Komponente im Netzwerk: den Load Balancer. Der Load Balancer wird zum Zugriffspunkt für den gesamten Cluster und leitet die Verbindungen von Clients zu einem der Broker im Cluster.

Wenn ein Client über den Load Balancer mit einem Broker (z. B. Knoten1) verbunden, dann getrennt und später wieder verbunden wird, besteht die Möglichkeit, dass die neue Verbindung zu einem anderen Broker im Cluster (z. B. Knoten3) geleitet wird. In diesem Fall sollte node3 damit beginnen, ausstehende Nachrichten an den Client zu senden, während der Client getrennt war.

Es gibt einige unterschiedliche Strategien zum Implementieren von clusterweiten persistenten Sitzungen. Beispielsweise kann sich der gesamte Cluster einen globalen Speicher teilen, der die Sitzungen der Clients fortbesteht.

Skalierbarere Lösungen gehen dieses Problem jedoch typischerweise auf verteilte Weise an, dh es findet eine Datenmigration von einem Knoten zu einem anderen statt. Diese Migration wird Sitzungsübernahme genannt. Die Sitzungsübernahme sollte für Kunden völlig transparent sein, hat jedoch ihren Preis, insbesondere wenn viele Nachrichten zu verschieben sind.

Sitzungsübernahme

Sticky Session zur Rettung

Das Wort „sticky“ bezieht sich hier auf die Fähigkeit des Load Balancers, den Client bei der Wiederverbindung an den alten Broker weiterzuleiten, wodurch eine Sitzungsübernahme vermieden werden kann. Dies ist eine besonders nützliche Funktion, wenn sich viele Clients ungefähr zur gleichen Zeit wieder verbinden oder wenn ein problematischer Client wiederholt die Verbindung trennt und erneut verbindet.

Damit der Load Balancer Verbindungen „sticky“ weiterleiten kann, muss der Broker die Client-ID (oder manchmal den Benutzernamen) in der Verbindungsanforderung kennen – dazu muss der Load Balancer MQTT-Pakete untersuchen, um nach solchen Informationen zu suchen.

Sobald die Client-ID (oder der Benutzername) für einen Cluster mit statischer Größe abgerufen wurde, kann der Broker die Client-ID (oder den Benutzernamen) in eine Broker-ID hashen. Für mehr Flexibilität kann der Load Balancer auch eine Zuordnungstabelle von der Client-ID (oder dem Benutzernamen) zur Zielknoten-ID verwalten.

Im nächsten Abschnitt demonstrieren wir eine Sticky-Table-Strategie in HAProxy 2.4.

Sticky-Session mit HAProxy 2.4

Um die Voraussetzungen zu minimieren, starten wir in diesem Demo-Cluster zwei EMQX-Knoten und einen HAProxy 2.4 in Docker-Containern.

Erstellen Sie ein Docker-Netzwerk

Damit sich die Container miteinander verbinden können, erstellen wir für sie ein Docker-Netzwerk.

docker network create test.net

Starten Sie zwei EMQX 4.3-Knoten

Damit sich die Knoten miteinander verbinden können, sollten der Containername und der EMQX-Knotenname innerhalb des Netzwerknamensraums (test.net).

Node1 starten

docker run -d \
    --name n1.test.net \
    --net test.net \
    -e EMQX_NODE_NAME=emqx@n1.test.net \
    -e EMQX_LISTENER __TCP__ EXTERNAL__PROXY_PROTOCOL=on \
    emqx/emqx:4.3.7

Node2 starten

docker run -d \
    --name n2.test.net \
    --net test.net \
    -e EMQX_NODE_NAME=emqx@n2.test.net \
    -e EMQX_LISTENER __TCP__ EXTERNAL__PROXY_PROTOCOL=on \
    emqx/emqx:4.3.7

Beachten Sie die Umgebungsvariable

EMQX_LISTENER __TCP__ EXTERNAL__PROXY_PROTOCOL. Es soll das binäre Proxy-Protokoll für TCP-Listener aktivieren, damit der Broker Informationen wie die echte IP-Adresse des Clients anstelle der des Load Balancers erhalten kann.

Lassen Sie EMQX-Knoten einem Cluster beitreten

docker exec -it n2.test.net emqx_ctl cluster join emqx@n1.test.net

Wenn alles wie erwartet läuft, sollte ein solches Protokoll gedruckt werden

[EMQX] emqx shutdown for join
Join the cluster successfully.
Cluster status: #{running_nodes => ['emqx@n1.test.net','emqx@n2.test.net'], stopped_nodes => []}

Starten Sie HAProxy 2.4

Erstellen Sie eine Datei /tmp/haproxy.configmit untenstehendem Inhalt

global
    log stdout format raw daemon debug
    nbproc 1
    nbthread 2
    cpu-map auto:1/1-2 0-1
    # Enable the HAProxy Runtime API
    # e.g. echo "show table emqx_tcp_back" | sudo socat stdio tcp4-connect:172.100.239.4:9999
    stats socket :9999 level admin expose-fd listeners

defaults
    log global
    mode tcp
    option tcplog
    maxconn 1024000
    timeout connect 30000
    timeout client 600s
    timeout server 600s

frontend emqx_tcp
   mode tcp
   option tcplog
   bind *:1883
   default_backend emqx_tcp_back

backend emqx_tcp_back
    mode tcp

    # Create a stick table for session persistence
    stick-table type string len 32 size 100k expire 30m

    # Use ClientID / client_identifier as persistence key
    stick on req.payload(0,0),mqtt_field_value(connect,client_identifier)

    # send proxy-protocol v2 headers
    server emqx1 n1.test.net:1883 check-send-proxy send-proxy-v2
    server emqx2 n2.test.net:1883 check-send-proxy send-proxy-v2

Haproxy im Test-Docker-Netzwerk starten:

docker run -d \
    --net test.net \
    --name proxy.test.net \
    -p 9999:9999 \
    -v /tmp/haproxy.cfg:/haproxy.cfg \
    haproxy:2.4 haproxy -f /haproxy.cfg

Testen Sie es aus

Jetzt verwenden wir den beliebten mosquitto MQTT-Client (ebenfalls im Docker), um es zu testen.

Wir starten einen Abonnenten (named subscriber1) die abonniert t/#Thema

docker run --rm -it --net test.net eclipse-mosquitto \
    mosquitto_sub -h proxy.test.net -t 't/#' -I subscriber1

Und veröffentlichen Sie dann a hello Nachricht an t/xyzvon einem anderen Kunden

docker run --rm -it --net test.net eclipse-mosquitto \
    mosquitto_pub -h proxy.test.net -t 't/xyz' -m 'hello'

Der Abonnent sollte ausdrucken hello Nachricht, ob alles wie erwartet funktioniert.

Überprüfen Sie die Sticky-Tabelle in HAProxy

Mit diesem Befehl können wir auch die in HAProxy erstellte Sticky-Tabelle überprüfen. Es benötigt socat Befehl, also führe ich ihn vom Docker-Host aus.

show table emqx_tcp_back" | sudo socat stdio tcp4-connect:127.0.0.1:9999

Dies sollte die aktuellen Verbindungen wie folgt drucken:

# table: emqx_external_tcp_listners, type: string, size:102400, used:1
0x7f930c033d90: key=subscriber1 use=0 exp=1793903 server_id=2 server_key=emqx2

In diesem Beispiel der Kunde subscriber1 hängt am Server emqx2.

Ursprünglich erschienen bei

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *