--Je connais GCP. -J'ai utilisé Firestore (ou Datastore) .. Je ne sais pas ce que c'est.
En supposant qu'il existe une application Web très ordinaire qui enregistre, met à jour et se réfère à certaines informations, je souhaite sauvegarder le contenu opéré à l'écran dans la base de données de mon système, et en même temps, conserver l'ordre des opérations et le refléter dans un autre système. Supposons qu'il y ait une exigence. Un autre système fournit une API pour refléter les données pour chaque fonction, vous pouvez donc y accéder, mais le problème est que chaque API est lourde (= en bref, le temps entre la réception d'une demande et la réponse est de 5 secondes ou plus. Ça prend). Par conséquent, en pensant simplement, il n'est pas possible d'atteindre l'API de manière synchrone comme c'est le cas dans le flux des opérations à l'écran. (Si vous faites cela, vous vous retrouverez avec un système qui attend environ 7 à 8 secondes chaque fois que l'utilisateur enregistre ou met à jour certaines informations.)
Ainsi, la partie qui atteint l'API est rendue asynchrone. Ensuite, la partie "" Garder l'ordre de fonctionnement "" "devient suspecte. Qu'est-ce que je devrais faire maintenant? Puisqu'il sera asynchrone pour le moment, voulez-vous mettre le contenu que vous souhaitez refléter dans la file d'attente des messages?
Cependant, si vous essayez d'utiliser ce qui précède, cela entre en conflit avec cette exigence dans les points suivants.
https://cloud.google.com/tasks/docs/common-pitfalls?hl=ja
De plus, en ce qui concerne la "possibilité d'exécution en double", il peut être correct de mettre à jour les mêmes données deux fois pour certains types de données, mais pour la création de nouvelles données, oui. Il n'y a pas. Deux données identiques seront créées. En d'autres termes, même si le message est dupliqué, un mécanisme pour l'éliminer doit être préparé séparément. Peut-être en général, donnez un identifiant unique à l'origine du message et utilisez-le comme "Memorystore" (https://cloud.google.com/memorystore?hl=ja) pour voir s'il a été traité. ) Etc. (S'il est déjà enregistré, incluez une implémentation qui considère ce message traité (c'est-à-dire dupliqué) et le supprime.)
Il est difficile de garantir l'ordre d'exécution, mais il est nécessaire de gérer l'exécution en double, et il y a trop d'éléments qui doivent être pris en compte et mis en œuvre pour ce que vous voulez faire. Donc, je pense plus simplement, "Est-il possible de conserver l'historique des opérations d'écran dans la base de données et de le traiter dans l'ordre de stockage (appuyez sur l'API d'un autre système)?" Cependant, je veux également considérer l'évolutivité. J'ai donc pensé à adopter Datastore, mais comme il est dit "Firestore est la nouvelle génération de datastore.", Essayons d'utiliser Firestore.
Je ne trouve pas toujours une bonne prune salée pour ce genre de thème. Cette fois, des informations telles que «école», «note», «classe», «enseignant» et «étudiant» peuvent être enregistrées. (C'est juste une pseudo chose.)
-- / add-school
・ ・ ・ Enregistrement de "l'école"
-- / add-grade
・ ・ ・ Enregistrement de "grade"
-- / add-class
・ ・ ・ Enregistrement de "classe"
-- / add-teacher
・ ・ ・ Enregistrement du "professeur"
-- / add-student
・ ・ ・ Inscription de "étudiant"
GOOGLE_APPLICATION_CREDENTIALS
.$ 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)
}
}
}
Commencez par vérifier les pièces suivantes.
Frappez les 5 points de terminaison suivants dans l'ordre du haut et répétez-le deux fois,
-- / add-school
・ ・ ・ Enregistrement de "l'école"
-- / add-grade
・ ・ ・ Enregistrement de "grade"
-- / add-class
・ ・ ・ Enregistrement de "classe"
-- / add-teacher
・ ・ ・ Enregistrement du "professeur"
-- / add-student
・ ・ ・ Inscription de "étudiant"
Certes, il y a des journaux qui ont été atteints dans l'ordre
Le Firestore a également beaucoup de documentation.
séquence
Cette fois, la partie suivante.
Les services de synchronisation suivants sont déployés.
Regardez le journal du conteneur pour voir s'il est en cours de traitement dans l'ordre chronologique.
J'ai fait deux tours dans l'ordre suivant, donc c'était comme prévu.
-- / add-school
・ ・ ・ Enregistrement de "l'école"
-- / add-grade
・ ・ ・ Enregistrement de "grade"
-- / add-class
・ ・ ・ Enregistrement de "classe"
-- / add-teacher
・ ・ ・ Enregistrement du "professeur"
-- / add-student
・ ・ ・ Inscription de "étudiant"
Dans le service de synchronisation, la lecture de / add-school
depuis le Firestore le met en veille pendant 5 secondes, mais bien sûr, il n'est pas dépassé par la lecture / add-grade
.
Comme ça, si vous mettez l'opération dans le Firestore dans l'ordre chronologique (il faut avoir un horodatage Unix de niveau Nano dans le champ où OrderBy peut être appliqué afin qu'il puisse être maintenu), ce sera dans un autre système tout en gardant l'ordre. Vous pouvez créer un système lié. Cependant, bien sûr, cela ne suffit pas à lui seul pour le mettre en production. Par exemple, les documents sont accumulés dans le Firestore (même s'il s'agit de documents inutiles une fois liés à un autre système), et donc, lorsque le service de synchronisation est redéployé, il aussi (bien qu'il ait été traité) 1 Il y a un problème pour récupérer le document et commencer à le traiter. À propos de ça Je pense que la solution simple est "N'est-il pas correct de supprimer le document à chaque fois que le processus est terminé?", Mais alors "Supprimer le document" a déclenché le processus pour terminer le traitement d'un document. Il arrive que le même document soit à nouveau traité (car il a été supprimé, une erreur se produit au milieu ou le deuxième processus s'exécute tel quel en fonction de la logique). donc, Il y a encore beaucoup de points à considérer, mais pour le moment, allons-y.