Warteschlangen sind ein großartiges Werkzeug, das sich fast perfekt skalieren lässt. Eisen kommt nicht zurecht? Wir haben dem Cluster gerade Knoten hinzugefügt. Wenn eine Warteschlange in einem Projekt vorhanden ist, ist es verlockend, mit seiner Hilfe immer mehr Funktionen zu implementieren.
Wir werden in diesem Artikel über die Fallstricke dieses Pfades sprechen.
Früher oder später, wenn Warteschlangen verwendet werden, steht der Benutzer vor der Frage, sie in Verbindung mit einem Dienst, einer Datenbank usw. zu verwenden.
Wenn die Bestellung abgeschlossen ist, müssen Sie eine SMS-Benachrichtigung an den Benutzer senden.
Wenn eine neue Bestellung eingegangen ist, müssen Sie eine Push-Benachrichtigung an die Ausführenden senden.
Wenn die Arbeit erledigt ist, müssen Sie das Geld vom Konto des Kunden abschreiben.
In allen oben genannten Beispielen werden Änderungen in einer Geschäftsentität in der Datenbank (oder einem Dienst mit einer Datenbank) aufgezeichnet, und es besteht die große Versuchung, Benachrichtigungen mithilfe von Warteschlangen zu senden.
Was haben wir in dieser Situation? Anfängliche, einfachste Codestruktur:
Der Dienst (unser Programm) zeichnet Datenänderungen in der Datenbank auf.
Der Dienst stellt den Job dann in die Warteschlange.
In diesem Fall müssen Sie einen Ereignisauslöser implementieren, um den Datensatz zu ändern.
Und im allgemeinen Fall stellt sich heraus, dass wir hier zwei Datensätze in zwei verschiedenen Datenbanken haben: Dienste und Warteschlangen.
Lassen Sie uns nun in die reale Welt springen und überlegen, welche Situationen auftreten können:
Alles in Ordnung. DB ist verfügbar, Warteschlange DB ist verfügbar;
, ;
, ;
, .
: , , .
, , ... . .
, .
, ( , , ), , :
.
.
.
.
, , .
:
, . 3 4 ( ).
. .
.
, . : , . , , .
, (, , http- /).
, .
/, ( queue
) .
, , ( ):
/* */
UPDATE
"orders"
SET
"status" = 'complete'
WHERE
"order_id" = $1
RETURNING
*
/* */
WITH "o" AS (
UPDATE
"orders"
SET
"status" = 'complete'
WHERE
"order_id" = $1
RETURNING
*
),
"q" AS (
INSERT INTO
"queue"
(
"key",
"data"
)
SELECT
"o.order_id",
"o.status"
FROM
"o"
)
SELECT
*
FROM
"o"
: queue
, , orders
.
, queue
, , . , .
:
queue
.
.
.
/
, , . , - ( , ..), :
.
, , , , O_APPEND
- .
( ) ,
.
( ) .
, , , , .
Wie Sie sehen, gibt es nur wenige Möglichkeiten, das Problem zu lösen. Wenn wir das System einfach halten wollen (KISS-Prinzip), führt die Einführung eines zusätzlichen Daemons und von Cache- / Protokollnachrichten in der Datenbank oder der lokalen Datei / Datenbank zu einer leichten Erhöhung der Komplexität. Gleichzeitig ist es sehr wichtig, den Handler idempotent zu halten, da bei Fehlern beim Übertragen von Aufgaben aus dem lokalen Cache in die allgemeine Warteschlange möglicherweise Duplikate auftreten.
Eine verallgemeinerte Lösung besteht darin, ein zweiphasiges Festschreiben zu verwenden.