Unter der Annahme, dass es eine sehr gewöhnliche Webanwendung gibt, die einige Informationen registriert, aktualisiert und auf diese verweist, möchte ich den auf dem Bildschirm angezeigten Inhalt in der Datenbank meines Systems speichern und gleichzeitig die Reihenfolge der Vorgänge beibehalten und in einem anderen System wiedergeben. Angenommen, es gibt eine Anforderung. Ein anderes System bietet eine API zum Reflektieren von Daten für jede Funktion, sodass Sie sie treffen können. Das Problem ist jedoch, dass jede API schwer ist (= kurz gesagt, die Zeit vom Empfang einer Anfrage bis zur Beantwortung beträgt 5 Sekunden oder mehr. Es braucht). Daher ist es nicht einfach möglich, die API synchron aufzurufen, da sie sich im Ablauf der Vorgänge auf dem Bildschirm befindet. (Wenn Sie dies tun, erhalten Sie ein System, das jedes Mal, wenn der Benutzer einige Informationen registriert oder aktualisiert, etwa 7 bis 8 Sekunden wartet.)
Der Teil, der auf die API trifft, wird also asynchron gemacht. Dann wird der Teil von "'Keep the order of operation`'" verdächtig. Was sollte ich jetzt tun? Möchten Sie den Inhalt, den Sie widerspiegeln möchten, in die Nachrichtenwarteschlange stellen, da er vorerst asynchron ist?
Wenn Sie jedoch versuchen, das oben Genannte zu verwenden, widerspricht dies dieser Anforderung in den folgenden Punkten.
https://cloud.google.com/tasks/docs/common-pitfalls?hl=ja
In Bezug auf die "Möglichkeit der doppelten Ausführung" kann es auch in Ordnung sein, dieselben Daten für bestimmte Datentypen zweimal zu aktualisieren, aber für die Erstellung neuer Daten, ja. Es gibt kein. Es werden zwei identische Daten erstellt. Mit anderen Worten, selbst wenn die Nachricht dupliziert wird, muss ein Mechanismus zum Entfernen separat erstellt werden. Geben Sie im Allgemeinen eine eindeutige ID am Ursprung der Nachricht an und verwenden Sie sie als "Memorystore" (https://cloud.google.com/memorystore?hl=ja), um festzustellen, ob sie verarbeitet wurde. ) Etc. (Wenn bereits registriert, fügen Sie eine Implementierung hinzu, die diese Nachricht als verarbeitet (dh dupliziert) betrachtet und löscht.)
Es ist schwierig, die Ausführungsreihenfolge zu garantieren, aber es ist erforderlich, sich mit doppelter Ausführung zu befassen, und es gibt zu viele Elemente, die für das, was Sie tun möchten, berücksichtigt und implementiert werden müssen. Ich denke also einfacher: "Ist es möglich, den Verlauf der Bildschirmoperationen in der Datenbank zu speichern und in der Reihenfolge des Speichers zu verarbeiten (klicken Sie auf die API eines anderen Systems)?" Ich möchte jedoch auch die Skalierbarkeit berücksichtigen. Also habe ich darüber nachgedacht, Datastore zu übernehmen, aber da dort steht "Firestore ist der Datenspeicher der nächsten Generation." Versuchen wir es mit Firestore.
Ich kann nicht immer eine gute Salzpflaume für diese Art von Thema finden. Dieses Mal können Informationen wie "Schule", "Klasse", "Klasse", "Lehrer" und "Schüler" registriert werden. (Es ist nur eine Pseudo-Sache.)
-- / add-grade
・ ・ ・ Registrierung von "grade"
-- / add-class
・ ・ ・ Registrierung von "class"
-- / add-lehrer
・ ・ ・ Registrierung von "Lehrer"
-- / add-student
・ ・ ・ Registrierung von "student"
--Go Entwicklungsumgebung wurde lokal erstellt.
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"
$ go version
go version go1.15.2 linux/amd64
IDE - Goland
GoLand 2020.2.3
Build #GO-202.7319.61, built on September 16, 2020
https://github.com/sky0621/go-publisher-fs/tree/v0.1.1
https://github.com/sky0621/go-subscriber-fs/tree/v0.1.1
main.go
package main
import (
"fmt"
"log"
"net/http"
"os"
"time"
"cloud.google.com/go/firestore"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
project := os.Getenv("PUB_PROJECT")
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.GET("/add-school", handler(project, "add-school"))
e.GET("/add-grade", handler(project, "add-grade"))
e.GET("/add-class", handler(project, "add-class"))
e.GET("/add-teacher", handler(project, "add-teacher"))
e.GET("/add-student", handler(project, "add-student"))
e.Logger.Fatal(e.Start(":8080"))
}
func handler(project, path string) func(c echo.Context) error {
return func(c echo.Context) error {
ctx := c.Request().Context()
client, err := firestore.NewClient(ctx, project)
if err != nil {
log.Fatal(err)
}
defer client.Close()
order := fmt.Sprintf("%s:%s", path, createUUID())
_, err = client.Collection("operation").Doc(order).
Set(ctx, map[string]interface{}{
"order": order,
"sequence": time.Now().UnixNano(),
}, firestore.MergeAll)
if err != nil {
log.Fatal(err)
}
return c.String(http.StatusOK, order)
}
}
func createUUID() string {
u, err := uuid.NewRandom()
if err != nil {
log.Fatal(err)
}
return u.String()
}
main.go
package main
import (
"context"
"log"
"os"
"strings"
"time"
"cloud.google.com/go/firestore"
)
func main() {
ctx := context.Background()
client, err := firestore.NewClient(ctx, os.Getenv("PUB_PROJECT"))
if err != nil {
log.Fatal(err)
}
defer client.Close()
operationIter := client.Collection("operation").
Where("sequence", ">", 0).OrderBy("sequence", firestore.Asc).Snapshots(ctx)
defer operationIter.Stop()
for {
operation, err := operationIter.Next()
if err != nil {
log.Fatalln(err)
}
for _, change := range operation.Changes {
ope, err := change.Doc.Ref.Get(ctx)
if err != nil {
log.Fatalln(err)
}
d := ope.Data()
order, ok := d["order"]
if ok {
ods := strings.Split(order.(string), ":")
if len(ods) > 0 {
od := ods[0]
switch od {
case "add-school":
time.Sleep(5 * time.Second)
case "add-grade":
time.Sleep(4 * time.Second)
case "add-class":
time.Sleep(3 * time.Second)
case "add-teacher":
time.Sleep(2 * time.Second)
case "add-student":
time.Sleep(1 * time.Second)
}
}
}
log.Printf("[operation-Data] %#+v", d)
}
}
}
Überprüfen Sie zunächst die folgenden Teile.
Treffen Sie die folgenden 5 Endpunkte in der Reihenfolge von oben und wiederholen Sie sie zweimal.
-- / add-school
・ ・ ・ Registrierung von "Schule"
-- / add-grade
・ ・ ・ Registrierung von "grade"
-- / add-class
・ ・ ・ Registrierung von "class"
-- / add-lehrer
・ ・ ・ Registrierung von "Lehrer"
-- / add-student
・ ・ ・ Registrierung von "student"
Natürlich gibt es Protokolle, die in der richtigen Reihenfolge getroffen wurden.
Der Firestore hat auch viele Dokumentationen.
Diesmal der folgende Teil.
Die folgenden Synchronisierungsdienste werden bereitgestellt.
Sehen Sie sich das Containerprotokoll an, um festzustellen, ob es in chronologischer Reihenfolge verarbeitet wird.
Ich habe zwei Runden in der folgenden Reihenfolge gemacht, also war es wie erwartet.
-- / add-school
・ ・ ・ Registrierung von "Schule"
-- / add-grade
・ ・ ・ Registrierung von "grade"
-- / add-class
・ ・ ・ Registrierung von "class"
-- / add-lehrer
・ ・ ・ Registrierung von "Lehrer"
-- / add-student
・ ・ ・ Registrierung von "student"
Wenn Sie im Synchronisierungsdienst "/ add-school" aus dem Firestore lesen, setzen Sie es für 5 Sekunden in den Ruhezustand, aber natürlich können Sie nicht durch das Lesen von "/ add-grade" überholt werden.
Wenn Sie den Vorgang in chronologischer Reihenfolge in den Firestore einfügen (es ist erforderlich, einen Unix-Zeitstempel auf Nanoebene in dem Feld zu haben, in dem OrderBy angewendet werden kann, damit er beibehalten werden kann), befindet er sich in einem anderen System, während die Reihenfolge beibehalten wird. Sie können ein verknüpftes System erstellen. Dies allein reicht natürlich nicht aus, um es in Produktion zu bringen. Beispielsweise werden die Dokumente im Firestore akkumuliert (obwohl es sich um unnötige Dokumente handelt, sobald sie mit einem anderen System verknüpft sind). Wenn der Synchronisierungsdienst erneut bereitgestellt wird, wird dies auch (obwohl er verarbeitet wurde) 1 Es besteht das Problem, das Dokument aufzunehmen und mit der Verarbeitung zu beginnen. Darüber Ich denke, die einfache Lösung lautet: "Ist es nicht in Ordnung, das Dokument jedes Mal zu löschen, wenn der Prozess abgeschlossen ist?". Dann löste "Dokument löschen" den Prozess aus, um die Verarbeitung eines Dokuments abzuschließen. Es kommt vor, dass dasselbe Dokument erneut verarbeitet wird (da es gelöscht wurde, tritt in der Mitte ein Fehler auf oder der zweite Prozess wird je nach Logik ausgeführt). damit, Es gibt noch viele Punkte, über die man nachdenken muss, aber lassen Sie uns vorerst auf diesen Punkt eingehen.