kratos micro service framework mall actual user Service-1

Posted by CAPTAINCAPSLOCK on Mon, 07 Feb 2022 21:11:06 +0100

kratos micro service framework mall actual user Service-1

Note: kratos, proto, wire and other command tools have been installed here. It is recommended to take a look at the official file To avoid execution failure

  1. A brief introduction to kratos framework

    Please read the official file

  2. Create mall micro Service - User Service

    1. Enter the local go path directory, create a new directory and enter the shop directory
    2. After entering the shop directory, execute the kratos new user command
    3. Enter the user directory and execute the go mod download command to pull the project dependencies
    4. visit http://127.0.0.1:8000/helloworld/kratos See the response below to prove that your framework installation is successful
      {
      "message": "Hello kratos"
      }
  3. Some preparatory work

    1. Modify the api/helloworld directory to api/user

    2. Delete API / user / V1 / greeter pb. go,api/user/v1/greeter_grpc.pb_.go ,api/user/v1/greeter_http.pb_.go

    3. Modify or delete API / user / V1 / greeter The proto file is user Proto file, modified as follows:

      Please learn the basic syntax of proto by yourself. At present, the service here only provides a service interface for creating users, which will be added step by step later

      syntax = "proto3";
      
      package user.v1;
      
      import "google/protobuf/empty.proto";
      
      option go_package = "user/api/user/v1;v1";
      
      service User{
      rpc CreateUser(CreateUserInfo) returns (UserInfoResponse); // Create user
      }
      
      // User information
      message UserInfoResponse{
      int64 id = 1;
      string password = 2;
      string mobile = 3;
      string nickName = 4;
      uint64 birthday = 5;
      string gender = 6;
      int32 role = 7;
      }
      
      // Create fields required by users
      message  CreateUserInfo{
      string nickName = 1;
      string password = 2;
      string mobile = 3;
      }
    4. Newly generated user Interface information defined by proto

      1. Enter the root directory user and execute the make api command
      2. See the following files in the user/api/user/v1 / directory
        user.proto
        user.pb.go
        user_grpc.pb.go
        user_http.pb.go
  4. Modify profile

    1. Modify user / config / config Yaml file

      Modify the configuration corresponding to the database under data according to your local configuration

    2. New user / configs / registry Yaml file

      consul:
       address: 127.0.0.1:8500
       scheme: http
    3. Modify the user/internal/conf/conf.proto file and add a new one at the bottom of the file

      message Registry {
          message Consul {
              string address = 1;
              string scheme = 2;
            }
            Consul consul = 1;
      }
    4. Newly generated conf.pb Go file
      Enter the project root directory and execute the command make config

    5. Add consumer service tool

      The docker tool is used here

      docker run -d -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8600:8600/udp consul consul agent -dev -client=0.0.0.0

    6. Browser access http://127.0.0.1:8500/ Verify that the installation was successful

  5. Modify startup program
    1. Modify user / CMD / user / main Go file

     *
     *
     *
     var (
         // Name is the name of the compiled software.
       Name = "shop.user.service"
         // Version is the version of the compiled software.
         Version string
         // flagconf is the config flag.
         flagconf string
    
         id, _ = os.Hostname()
     )
    
     *
     *
     *
    
     func newApp(logger log.Logger, gs *grpc.Server, rr registry.Registrar) *kratos.App {
         return kratos.New(
             kratos.ID(id),
             kratos.Name(Name),
             kratos.Version(Version),
             kratos.Metadata(map[string]string{}),
             kratos.Logger(logger),
             kratos.Server(
                 gs,
             ),
             kratos.Registrar(rr),
         )
     }
    
     func main() {
     *
     *
     *
         var rc conf.Registry
         if err := c.Scan(&rc); err != nil {
             panic(err)
         }
    
         app, cleanup, err := initApp(bc.Server, &rc, bc.Data, logger)
     *
     *
     *
     }

    2. Modify user / CMD / wire Go file

    func initApp(*conf.Server, *conf.Registry, *conf.Data, log.Logger) (*kratos.App, func(), error) {
         panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
     }
    1. Modify the root user/makefile file
      stay go generate ./... Add code below
      wire:
      cd cmd/user/ && wire

      *Represents unmodified code

  6. Modify the file in the user/internal/data / directory

    1. Modify data The contents of the go file are as follows:

      gorm is introduced here, and ent is not used

      package data
      import (
       "github.com/go-kratos/kratos/v2/log"
       "github.com/google/wire"
       "gorm.io/driver/mysql"
       "gorm.io/gorm"
       "gorm.io/gorm/logger"
       "gorm.io/gorm/schema"
       slog "log"
       "os"
       "time"
       "user/internal/conf"
      )
      // ProviderSet is data providers.
      var ProviderSet = wire.NewSet(NewData, NewDB, NewUserRepo)
      type Data struct {
       db *gorm.DB
      }
      // NewData .
      func NewData(c *conf.Data, logger log.Logger, db *gorm.DB) (*Data, func(), error) {
       cleanup := func() {
           log.NewHelper(logger).Info("closing the data resources")
       }
       return &Data{db: db}, cleanup, nil
      }
      // NewDB .
      func NewDB(c *conf.Data) *gorm.DB {
       // Terminal print and input sql execution record
       newLogger := logger.New(
           slog.New(os.Stdout, "\r\n", slog.LstdFlags), // io writer
           logger.Config{
               SlowThreshold: time.Second, // Slow query SQL threshold
               Colorful:      true,        // Disable color printing
               //IgnoreRecordNotFoundError: false,
               LogLevel: logger.Info, // Log lever
           },
       )
       db, err := gorm.Open(mysql.Open(c.Database.Source), &gorm.Config{
           Logger: newLogger,
           NamingStrategy: schema.NamingStrategy{
               SingularTable: true, // Whether to add s to the table name
           },
       })
       if err != nil {
           panic("failed to connect database")
       }
       return db
      }
    2. Modify or delete user / internal / data / greeter Go is user Go contents are as follows:

      package data
      
      import (
       "context"
       "crypto/sha512"
       "fmt"
       "github.com/anaskhan96/go-password-encoder"
       "github.com/go-kratos/kratos/v2/log"
       "google.golang.org/grpc/codes"
       "google.golang.org/grpc/status"
       "user/internal/biz"
      )
      
      type userRepo struct {
       data *Data
       log  *log.Helper
      }
      
      // NewUserRepo .
      func NewUserRepo(data *Data, logger log.Logger) biz.UserRepo {
       return &userRepo{
           data: data,
           log:  log.NewHelper(logger),
       }
      }
      
      // CreateUser .
      func (r *userRepo) CreateUser(ctx context.Context, u *biz.User) (*biz.User, error) {
       // Verify that the has been created
       var user biz.User
       result := r.data.db.Where(&biz.User{Mobile: u.Mobile}).First(&user)
       if result.RowsAffected == 1 {
           return nil, status.Errorf(codes.AlreadyExists, "User already exists")
       }
      
       user.Mobile = u.Mobile
       user.NickName = u.NickName
       user.Password = encrypt(u.Password) // Password encryption
       res := r.data.db.Create(&user)
       if res.Error != nil {
           return nil, status.Errorf(codes.Internal, res.Error.Error())
       }
       userInfoRes := modelToResponse(user)
       return &userInfoRes, nil
      }
      
      // Password encryption
      func encrypt(psd string) string {
       options := &password.Options{SaltLen: 16, Iterations: 10000, KeyLen: 32, HashFunction: sha512.New}
       salt, encodedPwd := password.Encode(psd, options)
       return fmt.Sprintf("$pbkdf2-sha512$%s$%s", salt, encodedPwd)
      }
      
      // ModelToResponse transforms the values of all fields in the user table
      func modelToResponse(user biz.User) biz.User {
       userInfoRsp := biz.User{
           ID:       user.ID,
           Mobile:   user.Mobile,
           Password: user.Password,
           NickName: user.NickName,
           Gender:   user.Gender,
           Role:     user.Role,
           Birthday: user.Birthday,
       }
       return userInfoRsp
      }
  7. Modify the file in the user/internal/biz / directory

    1. Modify greeter The go file is user Go file, as follows:

      package biz
      
      import (
       "context"
       "github.com/go-kratos/kratos/v2/log"
       "gorm.io/gorm"
       "time"
      )
      
      type User struct {
       ID          int64      `gorm:"primarykey"`
       Mobile      string     `gorm:"index:idx_mobile;unique;type:varchar(11) comment 'Mobile phone number, unique user ID';not null"`
       Password    string     `gorm:"type:varchar(100);not null "` // Whether the user password is encrypted should be paid attention to when saving it
       NickName    string     `gorm:"type:varchar(25) comment 'User nickname'"`
       Birthday    *time.Time `gorm:"type:datetime comment 'Date of birth'"`
       Gender      string     `gorm:"column:gender;default:male;type:varchar(16) comment 'female:female,male:male'"`
       Role        int        `gorm:"column:role;default:1;type:int comment '1:Ordinary users, 2:administrators'"`
       CreatedAt   time.Time  `gorm:"column:add_time"`
       UpdatedAt   time.Time  `gorm:"column:update_time"`
       DeletedAt   gorm.DeletedAt
       IsDeletedAt bool
      }
      
      type UserRepo interface {
       CreateUser(context.Context, *User) (*User, error)
      }
      
      type UserUsecase struct {
       repo UserRepo
       log  *log.Helper
      }
      
      func NewUserUsecase(repo UserRepo, logger log.Logger) *UserUsecase {
       return &UserUsecase{repo: repo, log: log.NewHelper(logger)}
      }
      
      func (uc *UserUsecase) Create(ctx context.Context, u *User) (*User, error) {
       return uc.repo.CreateUser(ctx, u)
      }
    2. Modify biz Go file, as follows:

      package biz
      
      import "github.com/google/wire"
      // ProviderSet is biz providers.
      var ProviderSet = wire.NewSet(NewUserUsecase)
    3. New user Go file, directory user / internal / biz / entity / user Go contents are as follows:

      It is assumed that the table corresponding to the database has been created, and the database name is shop_ The user table name is user

      package main
      
       import (
           "gorm.io/driver/mysql"
           "gorm.io/gorm"
           "gorm.io/gorm/logger"
           "gorm.io/gorm/schema"
           "log"
           "os"
           "time"
           "user/internal/biz"
       )
      
       // Linked database
       func main() {
           dsn := "root:root@tcp(127.0.0.1:3306)/shop_user?charset=utf8mb4&parseTime=True&loc=Local"
           newLogger := logger.New(
               log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
               logger.Config{
                   SlowThreshold: time.Second, // Slow SQL threshold
                   LogLevel:      logger.Info, // Log level
                   Colorful:      true,        // Disable color printing
               },
           )
      
           // Global mode
           db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
               NamingStrategy: schema.NamingStrategy{
                   SingularTable: true,
               },
               Logger: newLogger,
           })
           if err != nil {
               panic(err)
           }
           _ = db.AutoMigrate(&biz.User{})
       }
  8. Modify the file in / user / internal directory

    1. Modify or delete user / internal / service / greeter Go is user Go, as follows:

      package service
      
      import (
       "context"
       "github.com/go-kratos/kratos/v2/log"
       v1 "user/api/user/v1"
       "user/internal/biz"
      )
      
      type UserService struct {
       v1.UnimplementedUserServer
      
       uc  *biz.UserUsecase
       log *log.Helper
      }
      
      // NewUserService new a greeter service.
      func NewUserService(uc *biz.UserUsecase, logger log.Logger) *UserService {
       return &UserService{uc: uc, log: log.NewHelper(logger)}
      }
      
      // CreateUser create a user
      func (u *UserService) CreateUser(ctx context.Context, req *v1.CreateUserInfo) (*v1.UserInfoResponse, error) {
       user, err := u.uc.Create(ctx, &biz.User{
           Mobile:   req.Mobile,
           Password: req.Password,
           NickName: req.NickName,
       })
       if err != nil {
           return nil, err
       }
      
       userInfoRsp := v1.UserInfoResponse{
           Id:       user.ID,
           Mobile:   user.Mobile,
           Password: user.Password,
           NickName: user.NickName,
           Gender:   user.Gender,
           Role:     int32(user.Role),
       }
       if user.Birthday != nil {
           userInfoRsp.Birthday = uint64(user.Birthday.Unix())
       }
       return &userInfoRsp, nil
      }
    2. Modify service Go file, as follows:

      package service
      
      import "github.com/google/wire"
      
      // ProviderSet is service providers.
      var ProviderSet = wire.NewSet(NewUserService)
  9. Modify the file in the user/internal/server / directory

    1. Delete http Go file

    2. Modify grpc The contents of the go file are as follows:

      package server
      
      import (
       "github.com/go-kratos/kratos/v2/log"
       "github.com/go-kratos/kratos/v2/middleware/recovery"
       "github.com/go-kratos/kratos/v2/transport/grpc"
       v1 "user/api/user/v1"
       "user/internal/conf"
       "user/internal/service"
      )
      
      // NewGRPCServer new a gRPC server.
      func NewGRPCServer(c *conf.Server, u *service.UserService, logger log.Logger) *grpc.Server {
       var opts = []grpc.ServerOption{
           grpc.Middleware(
               recovery.Recovery(),
           ),
       }
       if c.Grpc.Network != "" {
           opts = append(opts, grpc.Network(c.Grpc.Network))
       }
       if c.Grpc.Addr != "" {
           opts = append(opts, grpc.Address(c.Grpc.Addr))
       }
       if c.Grpc.Timeout != nil {
           opts = append(opts, grpc.Timeout(c.Grpc.Timeout.AsDuration()))
       }
       srv := grpc.NewServer(opts...)
       v1.RegisterUserServer(srv, u)
       return srv
      }
    3. Modify server Go contents are as follows:

      package server
      
      import (
       "github.com/go-kratos/kratos/v2/registry"
       "github.com/google/wire"
       "user/internal/conf"
      
       consul "github.com/go-kratos/kratos/contrib/registry/consul/v2"
       consulAPI "github.com/hashicorp/consul/api"
      )
      
      // ProviderSet is server providers.
      var ProviderSet = wire.NewSet(NewGRPCServer, NewRegistrar)
      
      // NewRegistrar introduces consumer
      func NewRegistrar(conf *conf.Registry) registry.Registrar {
       c := consulAPI.DefaultConfig()
       c.Address = conf.Consul.Address
       c.Scheme = conf.Consul.Scheme
      
       cli, err := consulAPI.NewClient(c)
       if err != nil {
           panic(err)
       }
       r := consul.New(cli, consul.WithHealthCheck(false))
       return r
      }
  10. Execute wire command
    Execute make wire from the root directory

  11. Execute the command to create a table from the root directory
    go run internal/biz/entity/user.go

  12. Start program

    Execute command from root directory
    kratos run
  13. Simple test service

    1. New user Go file user / test / user go
    2. user. The contents of the go file are as follows:
      package main
      import (
          "context"
          "fmt"
          "google.golang.org/grpc"
          v1 "user/api/user/v1"
      )
      var userClient v1.UserClient
      var conn *grpc.ClientConn
      func main() {
          Init()
          TestCreateUser() // Create user
          conn.Close()
      }
      // Init initialization link
      func Init() {
      var err error
      conn, err = grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())
      if err != nil {
          panic("grpc link err" + err.Error())
      }
      userClient = v1.NewUserClient(conn)
      }
      // TestCreateUser test creates 10 user data
      func TestCreateUser() {
      for i := 0; i < 10; i++ {
          rsp, err := userClient.CreateUser(context.Background(), &v1.CreateUserInfo{
              Mobile:   fmt.Sprintf("1350116722%d", i),
              Password: "admin",
              NickName: fmt.Sprintf("YW%d", i),
          })
          if err != nil {
              panic("grpc Failed to create user" + err.Error())
          }
          fmt.Println(rsp.Id)
      }
      }
    3. Query your own database to see if there is any inserted data.

The next task is to gradually improve the user service interface

Topics: Go