Out of the box micro service framework go zero (Advanced)

Posted by Pobega on Sun, 23 Jan 2022 22:37:19 +0100

We briefly introduced go zero before. See go Zero: out of the box micro service framework for details. This time, we will start from the user module of a Blog project and describe the use of go zero in detail.

In particular, all materials involved in this article have been uploaded to Github warehouse "kougazhan / go zero demo", and interested students can download them by themselves.

Go zero combat project: blog

Taking the website background of blog as an example, this paper focuses on how to use go zero to develop the user module of blog.

User module is a common module in the background management system, and its functions are also very familiar to everyone. Managing users involves front-end operations, and the persistence of user information is inseparable from the database. Therefore, the user module can be described as "although the sparrow is small, it has five internal organs". This article will describe in detail how to use go zero to complete the user module functions, such as user login, adding users, deleting users, modifying users, querying users, etc. (please refer to the warehouse code for the complete Api document)

Overall Blog architecture

At the top is the api gateway layer. Go zero needs the api gateway layer to proxy the request, and forward the request to the corresponding rpc service through gRPC for processing. The business logic of forwarding the specific request to the corresponding rpc service needs handwriting.

Next is the rpc service layer. The user in the rpc service in the figure above is the module to be demonstrated to you next. Each rpc service can be deployed separately. After the service is started, the relevant information will be registered to ETCD, so that the api gateway layer can find the address of the specific service through ECTD. The rpc service handles the business logic of specific requests, which needs handwriting.

Finally, the model layer. The model layer encapsulates the related logic of database operation. If it is a query class related operation, you will first query whether there is a corresponding cache in redis. For non query operations, MySQL will be directly operated. Goctl can generate ordinary CRDU code through sql files. As mentioned above, currently goctl only supports mysql.

The following shows how to use go zero to develop a user module of blog system.

api gateway layer

Write a blog API file

  • Generate blog API file

Execute the command goctl API - O blog API, create blog API file.

  • Role of api files

Please refer to the documentation for the detailed syntax of API files[ https://go-zero.dev/cn/api-grammar.html ], this article talks about the function and basic syntax of API files according to personal understanding.

The api file is used to generate the relevant code of the api gateway layer.

  • Syntax of api files

The syntax of the api file is very similar to that of the Golang language. The type keyword is used to define the structure, and the service part is used to define the api service.

The structure defined by type is mainly used to declare the input parameter and return value of the request, that is, request and response

The api service defined by service declares the route, handler, request and response

Please understand the specific contents in combination with the following default generated api files.

// Declared version, ignored
syntax = "v1"

// Declare some project information, which can be ignored
info(
   title: // TODO: add title
   desc: // TODO: add description
   author: "zhao.zhang"
   email: "zhao.zhang@upai.com"
)

// Important configuration
// request is the name of the structure. You can use the type keyword to define a new structure
type request {
   // TODO: add members here and delete this comment
   // Consistent with the golang language, the members of the structure are declared here
}

// The syntax is the same as above, but the business meaning is different. response is generally used to declare the return value.
type response {
   // TODO: add members here and delete this comment
}

// Important configuration
// Blog API is the name of the service
service blog-api {
   // GetUser is the view function that handles the request
   @handler GetUser // TODO: set handler name and delete this comment
   // GET declares that the request uses the GET method
   // /users/id/:userId is url, and: userId indicates a variable
   // Request is the request defined by type above and is the input parameter of the request
   // Response is the response defined by type above, which is the return value of the request.
   get /users/id/:userId(request) returns(response)

   @handler CreateUser // TODO: set handler name and delete this comment
   post /users/create(request)
}
  • Write a blog API file

Considering the length of the article, consider the complete blog For API files, refer to the repository on gitee. The code generated below is based on the blog on the warehouse Generated by API file.

api related code

  • Generate relevant code

Execute the command goctl API go - API blog api -dir . , Generate API related code.

  • Directory introduction
├── blog.api # api file
├── blog.go # Program entry file
├── etc
│   └── blog-api.yaml # api gateway layer configuration file
├── go.mod
├── go.sum
└── internal
    ├── config
    │   └── config.go # configuration file
    ├── handler # In the view function layer, the handler file corresponds to the following logic file one by one
    │   ├── adduserhandler.go
    │   ├── deleteuserhandler.go
    │   ├── getusershandler.go
    │   ├── loginhandler.go
    │   ├── routes.go
    │   └── updateuserhandler.go
    ├── logic # Where code needs to be populated manually
    │   ├── adduserlogic.go
    │   ├── deleteuserlogic.go
    │   ├── getuserslogic.go
    │   ├── loginlogic.go
    │   └── updateuserlogic.go
    ├── svc # Where rpc objects are encapsulated, they will be
    │   └── servicecontext.go
    └── types # Blog The structure defined in the API is mapped to the real golang structure
        └── types.go
  • Calling relationship between files

Since the rpc service has not been involved at this time, the calling relationship of each module in the api is a very simple calling relationship between single applications. routers.go is a route. The request is distributed to the corresponding handler according to the request Method and url. The handler will call the corresponding logic The logic file is where we inject code logic.

Summary

Api layer related commands:

  • Execute the command goctl API - O blog API, create blog API file.

  • Execute the command goctl API go - API blog api -dir . , Generate API related code.

  • Adding the parameter goctl can also generate api layer files of other languages, such as java and ts. after trying, it is difficult to use, so it will not be expanded.

rpc service

Write proto file

  • Generate user Proto file

Use the command goctl RPC template - O user Proto, generate user Proto file

  • user. Role of proto file

user.proto is used to generate the relevant codes of rpc services.

The syntax of protobuf has gone beyond the scope of go zero, so I won't expand it in detail here.

  • Write user Proto file

Considering the length of the article, consider the complete user For proto files, please refer to the repository on gitee.

Generate rpc related code

  • Generate user rpc service related codes

Use the command goctl RPC proto - SRC user proto -dir . Generate code for user rpc service.

Summary

rpc service related commands:

  • Use the command goctl RPC template - O user Proto, generate user Proto file

  • Use the command goctl RPC proto - SRC user proto -dir . Generate code for user rpc service.

api service calls rpc service

A: Why is this section arranged behind the rpc service?

Q: Because the content body of the logic part is to call the corresponding user rpc service, we must start this part after the user rpc code has been generated.

A: Steps of calling rpc service in api gateway layer

Q: If the directory structure of this part is unclear, please refer to the previous "api gateway layer api related code directory introduction".

  • Edit the configuration file etc / blog API Yaml, configure information about rpc services.
Name: blog-api
Host: 0.0.0.0
Port: 8888
# Add user rpc service
User:
  Etcd:
#  Hosts is user value of RPC service in etcd  
    Hosts:
      - localhost:2379
# The key is user Key value of RPC service in etcd
    Key: user.rpc
  • Edit the file config / config go
type Config struct {
   rest.RestConf
   // Add manually
   // RpcClientConf is the configuration of rpc client, which is used to parse in blog API Configuration in yaml
   User zrpc.RpcClientConf
}
  • Edit the file internal / SVC / servicecontext go
type ServiceContext struct {
   Config config.Config
   // Add manually
   // users.Users is the exposed interface of user rpc service
   User   users.Users
}

func NewServiceContext(c config.Config) *ServiceContext {
   return &ServiceContext{
      Config: c,
      // Add manually
      //  zrpc. Mustnew client (C. user) creates a grpc client
      User:   users.NewUsers(zrpc.MustNewClient(c.User)),
   }
}
  • Edit each logic file with internal / logic / loginlogic Go as an example
func (l *LoginLogic) Login(req types.ReqUser) (*types.RespLogin, error) {
   // Call the login method of user rpc
   resp, err := l.svcCtx.User.Login(l.ctx, &users.ReqUser{Username: req.Username, Password: req.Password})
   if err != nil {
      return nil, err
   }
   return &types.RespLogin{Token: resp.Token}, nil
}

model layer

Writing sql files

Write the SQL file that creates the table user SQL and execute in the database.

CREATE TABLE `user`
(
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
  `username` varchar(255) NOT NULL UNIQUE COMMENT 'username',
  `password` varchar(255) NOT NULL COMMENT 'password',
  PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

Generate model related code

Run the command goctl model MySQL DDL - C - SRC user sql -dir ., The code of CRDU operating the database will be generated.

The model directory at this time:

├── user.sql # Handwritten
├── usermodel.go # Automatic generation
└── vars.go # Automatic generation

Notes on code generated by model

  • The model code uses spliced SQL statements, which may pose the risk of SQL injection.

  • The code for generating CRUD is relatively rudimentary, and we need to manually edit usermodel Go file to splice the SQL required by the business. See usermdel FindByName method in go.

rpc calls the code of the model layer

rpc directory structure

For rpc service, we only need to pay attention to the annotated files or directories below.

├── etc
│   └── user.yaml # The configuration file and database configuration are written here
├── internal
│   ├── config
│   │   └── config.go # config.go is the structure corresponding to yaml
│   ├── logic # Where business logic is populated
│   │   ├── createlogic.go
│   │   ├── deletelogic.go
│   │   ├── getalllogic.go
│   │   ├── getlogic.go
│   │   ├── loginlogic.go
│   │   └── updatelogic.go
│   ├── server
│   │   └── usersserver.go
│   └── svc
│       └── servicecontext.go # Encapsulate various dependencies
├── user
│   └── user.pb.go
├── user.go
├── user.proto
└── users
    └── users.go

Steps for rpc to call model layer code

  • Edit etc / user Yaml file
Name: user.rpc
ListenOn: 127.0.0.1:8080
Etcd:
  Hosts:
  - 127.0.0.1:2379
  Key: user.rpc
# The following are manually added configurations
# mysql configuration
DataSource: root:1234@tcp(localhost:3306)/gozero
# Corresponding table
Table: user
# redis as swap storage
Cache:
  - Host: localhost:6379
  • Edit internal / config / config Go file
type Config struct {
// zrpc.RpcServerConf indicates that it inherits the configuration of the rpc server
   zrpc.RpcServerConf
   DataSource string          // Manual code
   Cache      cache.CacheConf // Manual code
}
  • Edit internal / SVC / servicecontext Go, encapsulate dependencies such as model.
type ServiceContext struct {
   Config config.Config
   Model  model.UserModel // Manual code
}

func NewServiceContext(c config.Config) *ServiceContext {
   return &ServiceContext{
      Config: c,
      Model:  model.NewUserModel(sqlx.NewMysql(c.DataSource), c.Cache), // Manual code
   }
}
  • Edit the corresponding logic file with internal / logic / loginlogic Go as an example:
func (l *LoginLogic) Login(in *user.ReqUser) (*user.RespLogin, error) {
   // todo: add your logic here and delete this line
   one, err := l.svcCtx.Model.FindByName(in.Username)
   if err != nil {
      return nil, errors.Wrapf(err, "FindUser %s", in.Username)
   }

   if one.Password != in.Password {
      return nil, fmt.Errorf("user or password is invalid")
   }

   token := GenTokenByHmac(one.Username, secretKey)
   return &user.RespLogin{Token: token}, nil
}

Microservice demonstration run

We run the entire microservice in a stand-alone environment. We need to start the following services:

  • Redis

  • Mysql

  • Etcd

  • go run blog.go -f etc/blog-api.yaml

  • go run user.go -f etc/user.yaml

Among the above services, the rpc service must be started first, and then the gateway layer.

In the warehouse, I encapsulated start SH and stop SH script to run and stop microservices in a stand-alone environment.

Well, through the above six steps, the common functions of the blog user module are completed.

Finally, let's emphasize the key points. In addition to the common commands of goctl, there are rules to follow for the naming of go zero files. The configuration file is a yaml file placed in the etc directory. The corresponding structure of the yaml file is in interval / config / config Go. Dependency management is generally performed in interval / SVC / xxcontext Go. The place where we need to fill in the business logic is the file in the interval/logic directory.

Topics: architecture