1
头图

We use a series to explain the complete practice of microservices from requirements to online, from code to k8s deployment, from logging to monitoring, etc.

The whole project uses microservices developed by go-zero, which basically includes go-zero and some middleware developed by related go-zero authors. The technology stack used is basically the self-developed components of the go-zero project team, basically go -zero the whole family bucket.

Actual project address: https://github.com/Mikaelemmmm/go-zero-looklook

1. User Center Business Architecture Diagram

2. Dependencies

usercenter-api (user center api) depends on identity-rpc (authorization and authentication rpc), usercenter-rpc (user center rpc)

usercenter-rpc (user center rpc) depends on identity-rpc (authorization center rpc)

Let's look at the project usercenter/cmd/api/desc/usercenter.api, all the external http methods of user api are here

There are 4 business registration, login, obtaining user information, WeChat applet authorization

3. Registration example

1. Register the api service

When we write the api service code, we must first define the method in the service in usercenter.api, and then write the request and response in desc/user. The advantage of this splitting is that it is not so bloated.

a. Define the registration method in usercenter.api as follows

 // 用户模块v1版本的接口
@server(
    prefix: usercenter/v1
    group: user
)
service usercenter {
  @doc "注册"
    @handler register
    post /user/register (RegisterReq) returns (RegisterResp)
  
  .....
}

b. Define RegisterReq\RegisterResp in app/usercenter/cmd/api/desc/user/user.api

 type (
    RegisterReq {
        Mobile   string `json:"mobile"`
        Password string `json:"password"`
    }
    RegisterResp {
        AccessToken  string `json:"accessToken"`
        AccessExpire int64  `json:"accessExpire"`
        RefreshAfter int64  `json:"refreshAfter"`
    }
)

c, goctl generates api code

1) Enter the app/usercenter/cmd/api/desc directory from the command line.

2) Go to deploy/script/gencode/gen.sh in the project directory, copy the following command and execute it on the command line (the command line should be switched to the app/usercenter/cmd directory)

 $ goctl api go -api *.api -dir ../  -style=goZero

d. Open the app/usercenter/cmd/api/internal/logic/user/register.go file

It's very easy here, just call the user's rpc service directly

Here is a little trick. Many students feel that the fields returned by the rpc service are similar to the api definitions, and it is very troublesome to copy them manually every time. Does go have a tool like BeanCopyUtils.copy like java? The answer is definitely yes, you can see the above code copier.Copy, this library is another new work by the author of gorm, are you excited? Then let's continue to see what the rpc that calls the backend looks like.

2. Register rpc service

  • Define protobuf file

We create a new usercenter.proto in app/usercenter/cmd/rpc/pb and write the registration method

 // req 、resp
message RegisterReq {
  string mobile = 1;
  string nickname = 2;
  string password = 3;
  string authKey = 4;
  string authType = 5;
}

message RegisterResp {
  string accessToken = 1;
  int64  accessExpire = 2;
  int64  refreshAfter = 3;
}

// service
service usercenter {
  rpc register(RegisterReq) returns(RegisterResp);
  ...
}
  • Use goctl to generate code, you don't need to manually type here

1) Enter the app/usercenter/cmd/rpc/pb directory from the command line.

2) Go to deploy/script/gencode/gen.sh in the project directory, copy the following two commands, and execute them on the command line (the command line should be switched to the app/usercenter/cmd directory)

 $  goctl rpc protoc *.proto --go_out=../ --go-grpc_out=../  --zrpc_out=../
$  sed -i "" 's/,omitempty//g' *.pb.go
  • Open app/usercenter/cmd/rpc/internal/logic/registerLogic.go to write logic code

The registration is designed into 2 tables, a user table and a user_auth table. user stores basic user information, and user_auth is related information that can authorize login according to different platforms, so local transactions are designed here, because go-zero transactions need to be in It can only be used in the model, but I have done a process in the model and exposed it in the model, so it can be used in logic

The Trans method is defined in the model to expose the transaction to logic

Use directly in logic

Since the project supports applet and mobile phone number, the registration of applet does not require a password, so a process is done when processing the password. The password for the registration of the mobile phone number needs to be passed, and the password for the registration of the applet does not need to be passed. Empty to judge the api service when the mobile phone number is registered

After the usercenter-rpc is successfully registered, you need to request the token to log in to the front end, and directly request the identity-rpc to issue the user's token

identity-rpc is as follows

 message GenerateTokenReq {
  int64 userId = 1;
}
message GenerateTokenResp {
  string accessToken = 1;
  int64  accessExpire = 2;
  int64  refreshAfter = 3;
}


service identity{
  //生成token,只针对用户服务开放访问
  rpc generateToken(GenerateTokenReq) returns(GenerateTokenResp);
  .....
}

generatetokenlogic.go

 // GenerateToken 生成token,只针对用户服务开放访问.
func (l *GenerateTokenLogic) GenerateToken(in *pb.GenerateTokenReq) (*pb.GenerateTokenResp, error) {

    now := time.Now().Unix()
    accessExpire := l.svcCtx.Config.JwtAuth.AccessExpire
    accessToken, err := l.getJwtToken(l.svcCtx.Config.JwtAuth.AccessSecret, now, accessExpire, in.UserId)
    if err != nil {
        return nil, errors.Wrapf(ErrGenerateTokenError, "getJwtToken err userId:%d , err:%v", in.UserId, err)
    }

    //存入redis
    userTokenKey := fmt.Sprintf(globalkey.CacheUserTokenKey, in.UserId)
    err = l.svcCtx.RedisClient.Setex(userTokenKey, accessToken, int(accessExpire))
    if err != nil {
        return nil, errors.Wrapf(ErrGenerateTokenError, "SetnxEx err userId:%d, err:%v", in.UserId, err)
    }

    return &pb.GenerateTokenResp{
        AccessToken:  accessToken,
        AccessExpire: now + accessExpire,
        RefreshAfter: now + accessExpire/2,
    }, nil
}

The registration is successful and go to identity-rpc to get the token, token expiration time, and token replacement time to the api service

Fourth, the business obtains the login user id

When we obtain user information or place an order, we always need to obtain the id of the logged-in user. As we mentioned in the previous article, after we verify the token in the authorized identity service, the parsed userId will be returned to the header. nginx's authReuest

in file app/identity/cmd/api/internal/handler/verify/tokenHandler.go

When nginx accesses the back-end service through authRequest, it will pass the header content to the back-end service, because we have configured the following in nginx

In this case, we can get this userId in the back-end service. For example, we now visit usercenter/v1/user/detail to get the current login user information

ok, you can see that we can get it through ctxdata.GetUidFromCtx(l.ctx), why is it so magical? Let's click on this method

In fact, it is the userId obtained from ctx. Isn't it strange that we clearly put it in the header in nignx. Why can you get it through ctx in the business code of go?

1. [Tips] middleware

When nginx carries the x-user that is userId in the header to access the back-end service, the main function of our back-end service will load a global middleware when it starts, such as main in usercenter-api

app/usercenter/cmd/api/usercenter.go

The global middleware is defined here. As long as there is a request to a certain method of our usercenter-ap, it will enter the global middleware first. The specific content of the middleware is as follows

So do you understand at once, when we request usercenter/v1/user/detail, we will enter this middleware first. In this middleware, we get the parsed userId through X-User in the header of nginx and put it In ctx, when we continue to enter usercenter/v1/user/detail, can we directly take it out through ctx and use it in the business? All the truth is revealed.

The same is true for other user center services to log in, obtain login user information, and authorize log in with a small program. I won't go into too much detail here, just look at the code by yourself.

[Note] The applet is authorized to log in, remember to modify the configuration file, the configuration file here is fake, change it to your own

project address

https://github.com/zeromicro/go-zero

Welcome go-zero and star support us!

WeChat exchange group

Follow the official account of " Microservice Practice " and click on the exchange group to get the QR code of the community group.


kevinwan
931 声望3.5k 粉丝

go-zero作者