Erstellen eines Microservice-Blogs - Teil 3 „Benutzer“

Im zweiten Teil unserer Artikelserie „Schreiben eines Blogs zu Microservices“ haben wir das API-Gateway beschrieben .

Hier beschreiben wir die Implementierung des User Microservice.

Unser Microservice sollte in der Lage sein:

  • Protokollieren Sie Dienstaufrufe und Zwischenzustände, die TraceId angeben (die gleiche, die von api-gw ausgegeben wurde, siehe Teil 2 „API-Gateway“ ).
  • Implementieren Sie die Funktionen Login (SignIN) und Registration (SignUp)
  • Implementieren Sie CRUD-Funktionen (Erstellen, Lesen, Bearbeiten, Löschen von Datensätzen in der Datenbank). Verwenden Sie MongoDB als Datenbank.

Zunächst beschreiben wir unseren Service in der Proto-Datei (./services/user/protobuf/user.proto).
Geben Sie die verwendete Syntax an - proto3. Wir geben den Namen des Protobuf-Pakets an. In diesem Paket wird der automatisch generierte Code der Server- und Client-Teile implementiert.

Wir importieren die Anmerkungsbibliothek google / api / annotations.proto. Sie wird benötigt, um die Anweisungen zum Generieren der REST-API zu beschreiben.

syntax = "proto3"; package protobuf; import "google/api/annotations.proto"; 

Beschreibung des Benutzerdienstes, direkt die Schnittstellen (Methoden), die er haben soll. Beispiel: Die SignUp-Schnittstelle (Registrierung) empfängt eine SignUpRequest-Nachricht mit den Attributen Benutzername, Kennwort, Vorname und Nachname und antwortet mit einer SignUpResponse-Nachricht mit den Attributen Slug (UserID), Benutzername und Rolle. Geben Sie in der Schnittstellenbeschreibung im Abschnitt options die post-Direktive an: "/ api / v1 / user / signup. Basierend darauf erstellt der Codegenerator eine REST-Schnittstelle, die POST-Anforderungen unter http: {{api_gw_host}} / api / v1 / user empfängt / Anmelden.

 //-------------------------------------------------- //   User //-------------------------------------------------- service UserService { //  rpc SignUp (SignUpRequest) returns (SignUpResponse) { option (google.api.http) = { post: "/api/v1/user/signup" }; } … } //-------------------------------------------------- // SignUp //-------------------------------------------------- message SignUpRequest { string Username = 1; string Password = 2; string FirstName = 3; string LastName = 4; } message SignUpResponse { string Slug = 1; string Username = 2; string Role = 3; } 

Und es wird die folgende Struktur im Anforderungshauptteil erwarten:

 { Username: 'username_value', Password: 'password_value', FirstName: 'firstname_value', LastName: 'lastname_value', } 

Und dementsprechend, wenn erfolgreich, wird es die Struktur geben:

 { Slug: 'user_id_value', Username: 'username_value', Role: 'role_value', } 

Oder ein Fehler. Weitere Informationen zu Fehlern finden Sie etwas später in dem Abschnitt, in dem Funktionen beschrieben werden, mit denen die im Protokoll beschriebenen Schnittstellen implementiert werden.

Andere Schnittstellen (Anmelden, Erstellen, Aktualisieren, Löschen, Abrufen, Suchen) werden auf die gleiche Weise deklariert.

Jetzt haben wir ein fertiges Protokoll. Wir gehen in das Stammverzeichnis des Projekts und führen den Befehl sh ./bin/protogen.sh aus. Dieses Skript generiert den Hauptcode.
Wechseln Sie als Nächstes in das Verzeichnis ./services/user und schreiben Sie in die Datei functions.go die Implementierung der deklarierten Schnittstellen.

Zunächst implementieren wir Middleware. Bei jeder Anforderung an den Dienst ziehen wir die Parameter TraceId, UserId und UserRole aus dem Anforderungskontext heraus und schreiben sie in die Protokolldatei. Hier können Sie die Anforderungsberechtigung implementieren.

 //-------------------------------------------------- // Midelware //-------------------------------------------------- func AccessLogInterceptor(ctx context.Context,req interface{},info *grpc.UnaryServerInfo,handler grpc.UnaryHandler,) (interface{}, error) { start:=time.Now() md,_:=metadata.FromIncomingContext(ctx) // Calls the handler reply, err := handler(ctx, req) var traceId,userId,userRole string if len(md["trace-id"])>0{ traceId=md["trace-id"][0] } if len(md["user-id"])>0{ userId=md["user-id"][0] } if len(md["user-role"])>0{ userRole=md["user-role"][0] } msg:=fmt.Sprintf("Call:%v, traceId: %v, userId: %v, userRole: %v, time: %v", info.FullMethod,traceId,userId,userRole,time.Since(start)) app.AccesLog(msg) return reply, err } 

Bei der SignUp-Methode legen wir die Antwortstruktur fest.

 //,   STATUS_FAIL out:=&SignUpResponse{} 

Überprüfen Sie als Nächstes die Anforderungsparameter.

 //     // Username err:=checkUserName(in.Username) if err!=nil{ log.Printf("[ERR] %s.SignUp, %v", app.SERVICE_NAME,err) return out,err } // Username   err=o.checkUserNameExist(in.Username) if err!=nil{ log.Printf("[ERR] %s.SignUp, %v", app.SERVICE_NAME,err) return out,err } // Password err=checkPassword(in.Password) if err!=nil{ log.Printf("[ERR] %s.SignUp, %v", app.SERVICE_NAME,err) return out,err } 

Und wenn alles in Ordnung ist, füllen Sie die Benutzerstruktur aus, schreiben Sie in die Datenbank und geben Sie die Antwort zurück.

 user:=&User{ Username:in.Username, FirstName:in.FirstName, LastName:in.LastName, Password:getMD5(in.Password), } var slug string collection:= o.DbClient.Database("blog").Collection("users") insertResult, err := collection.InsertOne(context.TODO(), user) if err != nil { return out,err } if oid, ok := insertResult.InsertedID.(primitive.ObjectID); ok { slug=fmt.Sprintf("%s",oid.Hex()) }else { err:=app.ErrInsert return out,err } out.Slug=slug out.Username=in.Username out.Role=app.ROLE_USER return out,nil 

Separat achten wir auf die Fehlerrückgabe, zum Beispiel:

 err:=app.ErrInsert 

Da dieser Fehler letztendlich zu unserer API-WG (in ihrem REST-Teil) zurückkehrt, wäre es cool, ihn in einen Standard-HTTP-Antwortcode umzuwandeln. Um keinen zusätzlichen Code zu schreiben, sollten Sie nicht den Standard-go-Fehler, sondern status.error aus dem google.golang.org/grpc/status-Paket verwenden.

Alle typischen Fehler des User-Microservices und wie sie in HTTP-Antwortcodes konvertiert werden, sind in der Datei "/ Services / user / app / errors.go" beschrieben.

 package app import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) //    var ( //  ErrEmailIncorrect = status.Error(codes.InvalidArgument, " E-mail") ErrPasswordIsEmpty = status.Error(codes.InvalidArgument, "Password  ") ErrUserNameIsEmpty = status.Error(codes.InvalidArgument, "E-mail  ") ErrUserNameIsExist = status.Error(codes.AlreadyExists, "  ") ErrNotFound = status.Error(codes.NotFound, "  ") ErrIncorrectLoginOrPassword = status.Error(codes.Unauthenticated,"   ") // CRUD ErrInsert = status.Error(codes.Internal, "  ") ErrUpdate = status.Error(codes.Internal, "  ") ) //================================================== // All gRPC err codes //================================================== // codes.OK - http.StatusOK // codes.Canceled - http.StatusRequestTimeout // codes.Unknown - http.StatusInternalServerError // codes.InvalidArgument - http.StatusBadRequest // codes.DeadlineExceeded - http.StatusGatewayTimeout // codes.NotFound - http.StatusNotFound // codes.AlreadyExists - http.StatusConflict // codes.PermissionDenied - http.StatusForbidden // codes.Unauthenticated - http.StatusUnauthorized // codes.ResourceExhausted - http.StatusTooManyRequests // codes.FailedPrecondition - http.StatusBadRequest // codes.Aborted - http.StatusConflict // codes.OutOfRange - http.StatusBadRequest // codes.Unimplemented - http.StatusNotImplemented // codes.Internal - http.StatusInternalServerError // codes.Unavailable - http.StatusServiceUnavailable // codes.DataLoss - http.StatusInternalServerError 

Und das Letzte, was ich über den User Microservice erzählen möchte, ist, wie er gestartet wird und sich mit der Datenbank verbindet. Diese Vorgänge werden in der Datei ./services/user/main.go ausgeführt.

Servicestart:

 lis,err:= net.Listen("tcp", fmt.Sprintf(":%s", Port)) if err != nil { log.Fatalf("failed to listen: %v", err) } grpcServer:= grpc.NewServer( grpc.UnaryInterceptor(protobuf.AccessLogInterceptor), ) s:=&protobuf.Server{} … // attach the user service to the server protobuf.RegisterUserServiceServer(grpcServer, s) 

Verbindung zur Datenbank (main.go):

 //   s.DbConnect() defer s.DbDisconnect() 

Implementierung der DbConnect-Funktion (./services/user/functions.go):

 //-------------------------------------------------- // /   //-------------------------------------------------- func (o *Server) DbConnect() error { var client *mongo.Client // Create client strURI:=fmt.Sprintf("mongodb://%s:%s@%s:%s",os.Getenv("MONGO_USER"),os.Getenv("MONGO_PASS"),os.Getenv("MONGO_HOST"),os.Getenv("MONGO_PORT")) client, err:= mongo.NewClient(options.Client().ApplyURI(strURI)) if err != nil { return err } // Create connect err = client.Connect(context.TODO()) if err != nil { return err } o.DbClient=client return nil } 

Source: https://habr.com/ru/post/de482002/


All Articles