Microservice

[!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR

In the previous article, we have demonstrated how to quickly create a monolithic service. Next, let’s demonstrate how to quickly create a microservice. In this section, the api part is actually the same as the creation logic of the monolithic service, except that there is no communication between services in the monolithic service. And the api service in the microservice will have more rpc call configuration.

Forward

This section will briefly demonstrate with an order service calling user service. The demo code only conveys ideas, and some links will not be listed one by one.

Scenario summary

Suppose we are developing a mall project, and the developer Xiao Ming is responsible for the development of the user module (user) and the order module (order). Let’s split these two modules into two microservices.①

[!NOTE] ①: The splitting of microservices is also a science, and we will not discuss the details of how to split microservices here.

Demonstration function goal

  • Order service (order) provides a query interface
  • User service (user) provides a method for order service to obtain user information

Service design analysis

According to the scenario summary, we can know that the order is directly facing the user, and the data is accessed through the http protocol, and some basic data of the user needs to be obtained inside the order. Since our service adopts the microservice architecture design, Then two services (user, order) must exchange data. The data exchange between services is the communication between services. At this point, it is also a developer’s need to adopt a reasonable communication protocol. For consideration, communication can be carried out through http, rpc and other methods. Here we choose rpc to implement communication between services. I believe that I have already made a better scenario description of “What is the role of rpc service?”. Of course, there is much more than this design analysis before a service is developed, and we will not describe it in detail here. From the above, we need:

  • user rpc
  • order api

two services to initially implement this small demo.

Create mall project

  1. $ cd ~/go-zero-demo
  2. $ mkdir mall && cd mall

Create user rpc service

  • new user rpc

    1. $ cd ~/go-zero-demo/mall
    2. $ mkdir -p user/rpc&&cd user/rpc
  • Add user.proto file, add getUser method

    1. $ vim ~/go-zero-demo/mall/user/user.proto
    1. syntax = "proto3";
    2. package user;
    3. option go_package = "user";
    4. message IdRequest {
    5. string id = 1;
    6. }
    7. message UserResponse {
    8. string id = 1;
    9. string name = 2;
    10. string gender = 3;
    11. }
    12. service User {
    13. rpc getUser(IdRequest) returns(UserResponse);
    14. }
  • Generate code

    1. $ cd ~/go-zero-demo/mall/user/rpc
    2. $ goctl rpc proto -src user.proto -dir .
    3. [goclt version <=1.2.1] protoc -I=/Users/xx/mall/user user.proto --goctl_out=plugins=grpc:/Users/xx/mall/user/user
    4. [goctl version > 1.2.1] protoc -I=/Users/xx/mall/user user.proto --go_out=plugins=grpc:/Users/xx/mall/user/user
    5. Done.

[!TIPS] If the installed version of protoc-gen-go is larger than 1.4.0, it is recommended to add go_package to the proto file

  • Fill in business logic

    1. $ vim internal/logic/getuserlogic.go
    1. package logic
    2. import (
    3. "context"
    4. "go-zero-demo/mall/user/internal/svc"
    5. "go-zero-demo/mall/user/user"
    6. "github.com/tal-tech/go-zero/core/logx"
    7. )
    8. type GetUserLogic struct {
    9. ctx context.Context
    10. svcCtx *svc.ServiceContext
    11. logx.Logger
    12. }
    13. func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic {
    14. return &GetUserLogic{
    15. ctx: ctx,
    16. svcCtx: svcCtx,
    17. Logger: logx.WithContext(ctx),
    18. }
    19. }
    20. func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) {
    21. return &user.UserResponse{
    22. Id: "1",
    23. Name: "test",
    24. }, nil
    25. }

Create order api service

  • Create an order api service

    1. $ cd ~/go-zero-demo/mall
    2. $ mkdir -p order/api&&cd order/api
  • Add api file

    1. $ vim order.api
    1. type(
    2. OrderReq {
    3. Id string `path:"id"`
    4. }
    5. OrderReply {
    6. Id string `json:"id"`
    7. Name string `json:"name"`
    8. }
    9. )
    10. service order {
    11. @handler getOrder
    12. get /api/order/get/:id (OrderReq) returns (OrderReply)
    13. }
  • Generate order service
    1. $ goctl api go -api order.api -dir .
    2. Done.
  • Add user rpc configuration

    1. $ vim internal/config/config.go
    1. package config
    2. import "github.com/tal-tech/go-zero/rest"
    3. import "github.com/tal-tech/go-zero/zrpc"
    4. type Config struct {
    5. rest.RestConf
    6. UserRpc zrpc.RpcClientConf
    7. }
  • Add yaml configuration

    1. $ vim etc/order.yaml
    1. Name: order
    2. Host: 0.0.0.0
    3. Port: 8888
    4. UserRpc:
    5. Etcd:
    6. Hosts:
    7. - 127.0.0.1:2379
    8. Key: user.rpc
  • Improve service dependence

    1. $ vim internal/svc/servicecontext.go
    1. package svc
    2. import (
    3. "go-zero-demo/mall/order/api/internal/config"
    4. "go-zero-demo/mall/user/rpc/userclient"
    5. "github.com/tal-tech/go-zero/zrpc"
    6. )
    7. type ServiceContext struct {
    8. Config config.Config
    9. UserRpc userclient.User
    10. }
    11. func NewServiceContext(c config.Config) *ServiceContext {
    12. return &ServiceContext{
    13. Config: c,
    14. UserRpc: userclient.NewUser(zrpc.MustNewClient(c.UserRpc)),
    15. }
    16. }
  • Add order demo logic

    Add business logic to getorderlogic

    1. $ vim ~/go-zero-demo/mall/order/api/internal/logic/getorderlogic.go
    1. user, err := l.svcCtx.UserRpc.GetUser(l.ctx, &userclient.IdRequest{
    2. Id: "1",
    3. })
    4. if err != nil {
    5. return nil, err
    6. }
    7. if user.Name != "test" {
    8. return nil, errors.New("User does not exist")
    9. }
    10. return &types.OrderReply{
    11. Id: req.Id,
    12. Name: "test order",
    13. }, nil

Start the service and verify

  • Start etcd
    1. $ etcd
  • Start user rpc

    1. $ go run user.go -f etc/user.yaml
    1. Starting rpc server at 127.0.0.1:8080...
  • Start order api

    1. $ go run order.go -f etc/order.yaml
    1. Starting server at 0.0.0.0:8888...
  • Access order api

    1. curl -i -X GET \
    2. http://localhost:8888/api/order/get/1
    1. HTTP/1.1 200 OK
    2. Content-Type: application/json
    3. Date: Sun, 07 Feb 2021 03:45:05 GMT
    4. Content-Length: 30
    5. {"id":"1","name":"test order"}

[!TIP] The api syntax mentioned in the demo, how to use and install rpc generation, goctl, goctl environment, etc. are not outlined in detail in the quick start. We will have detailed documents to describe in the follow-up. You can also click on the following [Guess you think View] View the corresponding document for quick jump.

Source code

mall source code

Guess you wants