Menulis Blog Layanan Mikro - Bagian 3 “Pengguna”

Pada bagian kedua dari seri artikel kami “Menulis Blog di Layanan Mikro” kami menggambarkan API Gateway .

Di sini kami menjelaskan implementasi microservice pengguna.

Layanan microser kami harus dapat:

  • Log panggilan layanan dan status perantara yang menunjukkan TraceId (yang sama dikeluarkan oleh api-gw, lihat Bagian 2 "API Gateway" )
  • Menerapkan fungsi Login (SignIN) dan Registrasi (Signup)
  • Menerapkan fungsi CRUD (membuat, membaca, mengedit, menghapus catatan dalam database). Gunakan MongoDB sebagai database.

Pertama, kami menggambarkan layanan kami di file proto (./services/user/protobuf/user.proto).
Tentukan sintaks yang digunakan - proto3. Kami menunjukkan nama paket protobuf, dalam paket ini kode yang dibuat secara otomatis dari server dan bagian klien akan diimplementasikan.

Kami mengimpor perpustakaan anotasi google / api / annotations.proto, itu akan diperlukan untuk menggambarkan arahan untuk menghasilkan REST API.

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

Deskripsi layanan Pengguna, langsung antarmuka (metode) yang harus dimiliki. Misalnya, antarmuka SignUp (pendaftaran): ia menerima pesan SignUpRequest yang berisi atribut Username, Password, FirstName, dan LastName dan merespons dengan pesan SignUpResponse yang berisi atribut Slug (UserID), Username, Atribut peran. Juga di deskripsi antarmuka, di bagian opsi, tentukan direktif pos: "/ api / v1 / user / signup. Berdasarkan hal itu, pembuat kode akan membuat antarmuka REST yang akan menerima permintaan POST di http: {{api_gw_host}} / api / v1 / user / daftar.

 //-------------------------------------------------- //   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; } 

Dan itu akan mengharapkan struktur berikut di badan permintaan:

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

Dan dengan demikian, jika berhasil, itu akan memberikan struktur:

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

Atau kesalahan. Kami akan memberi tahu Anda lebih banyak tentang kesalahan beberapa saat kemudian di bagian yang menjelaskan fungsi yang mengimplementasikan antarmuka yang dijelaskan dalam protofile.

Antarmuka lain (Masuk, Buat, Perbarui, Hapus, Dapatkan, Temukan) dideklarasikan dengan cara yang sama.

Sekarang kita memiliki protofile yang sudah jadi. Kami pergi ke direktori root proyek dan menjalankan perintah sh ./bin/protogen.sh. Script ini akan menghasilkan kode utama.
Selanjutnya, pergi ke direktori ./services/user dan dalam file functions.go tulis implementasi dari antarmuka yang dideklarasikan.

Pertama, kami menerapkan middleware. Pada setiap permintaan ke layanan, kami mengeluarkan parameter TraceId, UserId, UserRole dari konteks permintaan dan menuliskannya ke file log. Di sini Anda dapat menerapkan otorisasi permintaan.

 //-------------------------------------------------- // 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 } 

Dalam metode Pendaftaran, kami menentukan struktur respons.

 //,   STATUS_FAIL out:=&SignUpResponse{} 

Selanjutnya, periksa parameter permintaan.

 //     // 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 } 

Dan jika semuanya baik-baik saja, isi struktur Pengguna, tulis ke database dan kembalikan jawabannya.

 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 

Secara terpisah, kami memperhatikan pengembalian kesalahan, misalnya:

 err:=app.ErrInsert 

Karena pada akhirnya kesalahan ini akan kembali ke api-wg kami (di bagian REST-nya) dan akan lebih baik untuk mengubahnya menjadi kode respons HTTP standar. Agar tidak menulis banyak kode tambahan, Anda tidak boleh menggunakan standard go error, tetapi status.error dari paket google.golang.org/grpc/status.

Semua kesalahan umum dari microservice pengguna dan bagaimana mereka dikonversi ke kode respons HTTP dijelaskan dalam file. / Layanan / pengguna / aplikasi / errors.go.

 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 

Dan hal terakhir yang ingin saya sampaikan tentang microservice pengguna adalah bagaimana ia memulai dan terhubung ke database. Operasi ini dilakukan dalam file ./services/user/main.go.

Peluncuran Layanan:

 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) 

Koneksi ke database (main.go):

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

Implementasi fungsi DbConnect (./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/id482002/


All Articles