Wer mit Shopware 6 arbeitet ist sicher schon mal über die Message Queue gestolpert. Diese sorgt dafür das Aufgaben die „warten können“ in einer Tabelle Form gespeichert werden und später abgearbeitet werden, aber mehr dazu im Beitrag.
Message Queuing
In der Informatik sind Message Queues und Mailboxen Kommunikationsprotokolle, die für die Interprozesskommunikation (IPC) oder für die Inter-Thread-Kommunikation innerhalb desselben Prozesses verwendet werden. Sie verwenden eine Warteschlange für die Nachricht (Messaging) und beinhalten Managementfunktionen zur Weitergabe der Kontrolle oder des Inhalts (Quelle: Wikipedia)
Symfony Messenger als Basis
Die Basis für die Message Queue von Shopware bildet die Symfony Messenger Komponente (finden könnt Ihr die Version in der composer.lock:
"symfony/messenger": "~4.4.13"
Da Shopware auf Symfony 4.4.* basiert werden die Komponenten in der Regel auch auf dieser Version laufen.
In Ihrer Doku erfahrt Ihr sicher noch das ein oder andere, was die Integration und Production angeht.
SaaS oder Selfhosted?
Saas
Einige Anbieter sahen vernünftig aus und liefen ganz gut, wenn Ihr nur einen Node braucht dann nehmt die günstige Variante von Stackhero – braucht Ihr hohe Verfügbarkeit und mehrere Node dann schaut euch mal CloudAMQP an.
wir fahren aktuell mit einer selbst gehosteten Lösung auf Digital Ocean ganz gut. Hier eine kurze Anleitung wie Ihr es unter Ubuntu 20.04 (Focal Fossa) installiert und startet.
Selfhosted
RabbitMQ basiert auf der Sprache Erlang („Erlang is a programming language used to build massively scalable soft real-time systems with requirements on high availability“ Quelle: Erlang) und muss vorab installiert sein.
Erl installieren
wget -O- https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | sudo apt-key add -
echo "deb https://packages.erlang-solutions.com/ubuntu focal contrib" | sudo tee /etc/apt/sources.list.d/rabbitmq.list
sudo apt update
sudo apt install erlang
mit erl tested Ihr ob alles funktioniert.
Erlang/OTP 23 [erts-11.1.7] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1]
Eshell V11.1.7 (abort with ^G)
RabbitMQ installieren
sudo apt update && sudo apt install wget -y
sudo apt install apt-transport-https -y
wget -O- https://dl.bintray.com/rabbitmq/Keys/rabbitmq-release-signing-key.asc | sudo apt-key add -
wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -
echo "deb https://dl.bintray.com/rabbitmq-erlang/debian focal erlang-22.x" | sudo tee /etc/apt/sources.list.d/rabbitmq.list
sudo apt update
sudo apt install rabbitmq-server
Start beim booten könnt Ihr so erzielen:
systemctl is-enabled rabbitmq-server.service
prüft ob es aktiv ist
sudo systemctl enable rabbitmq-server
aktiviert es beim Start.
Das Dashboard kann ich euch am Anfang ans Herz legen:
sudo rabbitmq-plugins enable rabbitmq_management
UFW sollte übrigens schon aktiviert sein, bei so zielmich jedem Server, daher fügt IHr die Regel noch hinzu:
sudo ufw allow proto tcp from any to any port 5672,15672
um den Traffic zu erlauben für RMQ.
User erstellen
rabbitmqctl add_user admin DEINPASSWORT
rabbitmqctl set_user_tags admin administrator
PS: Im Standard habt ihr einen User guest mit PW guest. Der kann aber nur über localhost connecten.
VHost erstellen und zuweisen
„RabbitMQ is multi-tenant system: connections, exchanges, queues, bindings, user permissions, policies and some other things belong to virtual hosts, logical groups of entities.“ (Quelle: RabbitMQ)
Wir haben ein Multishop Cluster (mehrere Shops in einem Cluster in unterschiedlichen Docroots und benötigen daher verschiedene VHosts). Habt Ihr nur einen Shop, benötigt Ihr diesen Schritt nicht und könnt gleich auf / gehen.
rabbitmqctl list_vhosts
(sollte nur einen ausgeben und zwar /)
rabbitmqctl add_vhost /SHOP
erstellt den VHost fischen
rabbitmqctl add_user BENUTZER PASSWORT
erstelle wie oben einen User
rabbitmqctl set_permissions -p /SHOP BENUTZER ".*" ".*" ".*"
erstelle Rechte für User auf dem VHost
Das war es „schon“ – sieht kompliziert ist aus, ist aber mit Erfahrung in 10 min gemacht.
Shopware 6 konfigurieren
Shopware hat hier bereits etwas in Ihrer Doku geschrieben.
Erstellen einer enqueue.yaml Datei im oben stehenden Pfad, ich lege die AMQP Url nicht in der enqueue.yaml Datei ab, da ich diese Daten nicht mit ins Repository pushen möchte.
# config/packages/enqueue.yaml
enqueue:
amqp:
transport: '%env(AMQP_URL)%'
client: ~
# .env
AMQP_URL="amqp://guest:guest@rabbitmq:5672/%2FVIRTUAL_HOST?connection_timeout=50&heartbeat=0"
bei guest:guest tragt Ihr entsprechend eure eben angelegten Daten ein und rabbitmq tauscht Ihr mit eurere IP oder Domain aus. Euren VHost müsst Ihr unbedingt so angeben wie oben, sonst wird dieser nicht gefunden (der mit aus URL encoded und nicht ist etwas strange mMn)
Erstellen einer framework.yaml Datei im oben stehenden Pfad, falls Ihr nicht schon eine habt für Redis o.Ä.
# config/packages/framework.yaml
framework:
messenger:
transports:
default: enqueue://amqp
In der Doku von Shopware steht das man eine eigene Queue anlegen soll, die Parameter Übergabe hat bei mir allerdings nicht geklappt, daher arbeite ich mit dem default Namen „messages“ und lege diese in meinem neuen VHost an.
Danach benötigt Ihr noch einen Exchange:
Und danach bindet Ihr diesen noch zu eurer Queue:
Danach sollte alles passen und Ihr könnt testen. Denkt daran den Cache zu leeren und etwas zu triggern was die queue füllt:
bin/console cache:clear
bin/console dal:refresh:index --use-queue -v
Danach sollte eure Message Rates im Bereich publish hochgehen.
Wenn Ihr jetzt noch eure worker startet, sollten die Messages entsprechend auf euren Servern konsumiert werden.
screen bin/console messenger:consume-messages default --memory-limit=512M --time-limit=180
# Startet einen Worker mit entweder 512MB Limit oder einem Zeitlimit von 180 Sekunden. Ich habe noch keine schönen Production Lösung gefunden um beim z.B. dal:refresh:index schnell 15-20 worker zu starten.
Aktuell haben wir dafür einen crontab
*/2 * * * * cd /home/www-data/shops/******/ && bin/console messenger:consume-messages default --memory-limit=512M --time-limit=180
Die Load auf dem Server hält sich in Grenzen:
Etwas strange ist das Verhalten am Ende der Verarbeitung, hier scheinen einige hundert bis tausend Messages nach weniger Sekunden verarbeitet oder ignoriert zu werden:
Fazit
Das Setup ist am Ende doch etwas aufwändiger geworden als ich dachte, da man sich erstmal in die Materie einlesen muss und die Begrifflichkeiten lernen muss. Allerdings führt hier bei größeren Setups kein Weg dran vorbei, die die DB locks doch zunehmen werden und wenn das ERP System 5000 Produktpreise per queue pusht, kann das teilweise zu Fehlern/Abbrüchen führen (zmd. wenn dies Standardmäßig das use-queue flag nutzt)
Ich würde mir wünschen, dass Shopware noch etwas mehr Infos auf Ihre Doku Seite packt. Die angegebene Konfiguration hat bei mir zmd. nicht funktioniert. Ich weiß, dass diese Infos auch selbst erarbeitet werden können, daher nutzt Shopware ja Standardkomponenten von Symfony. Sicher würde es aber den Einstieg in die Messenger Komponente vereinfachen. Alles in allem bin ich aber sehr glücklich darüber das Shopware bereits einige Aufgaben vorgefertigt in die queue packt. Eine Liste welche dies sind, gibt es später.
Nachtrag 31.05.2021
Für das abarbeiten von „delayed messages“ benötigt Ihr noch das Plugin https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases – wenn das nicht installiert ist, kann das dazu führen das eure Message queue hängen bleibt.
Bei mir musste die Datei in den Ordner
/usr/lib/rabbitmq/lib/rabbitmq_server-3.8.16/plugins
cp ~/rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez
und danach einfach aktivieren mit
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
Die einfachste Produktivlösung für n Worker dürfte supervisord (http://supervisord.org/) sein. Den setzen wir bereits seit Jahren erfolgreich für solche Dinge ein.