Skip to content

快速开始

单体服务

创建greet服务

1
2
3
4
5
6
$ mkdir go-zero-demo
$ cd go-zero-demo
$ go mod init go-zero-demo
$ goctl api new greet
$ go mod tidy
Done.

查看一下greet服务的目录结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ tree greet
greet
├── etc
│   └── greet-api.yaml
├── greet.api
├── greet.go
└── internal
    ├── config
    │   └── config.go
    ├── handler
    │   ├── greethandler.go
    │   └── routes.go
    ├── logic
    │   └── greetlogic.go
    ├── svc
    │   └── servicecontext.go
    └── types
        └── types.go

由以上目录结构可以观察到,greet服务虽小,但"五脏俱全"。接下来我们就可以在greetlogic.go中编写业务代码了。

编写逻辑

1
2
3
4
5
func (l *GreetLogic) Greet(req *types.Request) (resp *types.Response, err error) {
    return &types.Response{
        Message: "Hello go-zero",
    }, nil
}

启动并访问服务

启动服务

1
2
$ cd greet
$ go run greet.go -f etc/greet-api.yaml

输出如下,服务启动并侦听在8888端口:

1
Starting server at 0.0.0.0:8888...

访问服务

1
$ curl -i -X GET http://localhost:8888/from/you

返回如下:

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Traceparent: 00-2b3fad2495fe9ed5fcd912b64816d0ec-573ad10c4d1ca664-00
Date: Mon, 31 Oct 2022 05:35:05 GMT
Content-Length: 27

{"message":"Hello go-zero"}

微服务

假设我们在开发一个商城项目,而开发者小明负责用户模块(user)和订单模块(order)的开发,我们姑且将这两个模块拆分成两个微服务

  • 订单服务(order)提供一个查询接口
  • 用户服务(user)提供一个方法供订单服务获取用户信息

根据情景提要我们可以得知,订单是直接面向用户,通过http协议访问数据,而订单内部需要获取用户的一些基础数据,既然我们的服务是采用微服务的架构设计,
那么两个服务(user, order)就必须要进行数据交换,服务间的数据交换即服务间的通讯,到了这里,采用合理的通讯协议也是一个开发人员需要 考虑的事情,
可以通过http,rpc等方式来进行通讯,这里我们选择rpc来实现服务间的通讯,相信这里我已经对"rpc服务存在有什么作用?"已经作了一个比较好的场景描述。
当然,一个服务开发前远不止这点设计分析,我们这里就不详细描述了。从上文得知,我们需要一个

  • user rpc
  • order api

两个服务来初步实现这个小demo。

创建mall工程

1
2
3
4
➜  mkdir go-zero-mall
➜  cd go-zero-mall
➜  go mod init go-zero-mall
go: creating new go.mod: module go-zero-mall

创建user rpc服务

创建user rpc服务

1
mkdir -p mall/user/rpc

添加user.proto文件,增加getUser方法

mall/user/rpc/user.proto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
syntax = "proto3";

package user;

// protoc-gen-go 版本大于1.4.0, proto文件需要加上go_package,否则无法生成
option go_package = "./user";

message IdRequest {
    string id = 1;
}

message UserResponse {
    // 用户id
    string id = 1;
    // 用户名称
    string name = 2;
    // 用户性别
    string gender = 3;
}

service User {
    rpc getUser(IdRequest) returns(UserResponse);
}

生成代码

1
2
3
$ cd mall/user/rpc
$ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=.
Done.

填充业务逻辑

internal/logic/getuserlogic.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package logic

import (
    "context"

    "go-zero-demo/mall/user/rpc/internal/svc"
    "go-zero-demo/mall/user/rpc/types/user"

    "github.com/zeromicro/go-zero/core/logx"
)

type GetUserLogic struct {
    ctx    context.Context
    svcCtx *svc.ServiceContext
    logx.Logger
}

func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic {
    return &GetUserLogic{
        ctx:    ctx,
        svcCtx: svcCtx,
        Logger: logx.WithContext(ctx),
    }
}

func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) {
    return &user.UserResponse{
            Id:   "1",
            Name: "test",
    }, nil
}

创建order api服务

1
2
# 回到 go-zero-demo/mall 目录
$ mkdir -p order/api && cd order/api

添加api文件

order.api

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type(
    OrderReq {
        Id string `path:"id"`
    }

    OrderReply {
        Id string `json:"id"`
        Name string `json:"name"`
    }
)
service order {
    @handler getOrder
    get /api/order/get/:id (OrderReq) returns (OrderReply)
}

生成order服务

1
2
$ goctl api go -api order.api -dir .
Done.

添加user rpc配置

internal/config/config.go:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package config

import (
    "github.com/zeromicro/go-zero/zrpc"
    "github.com/zeromicro/go-zero/rest"
)

type Config struct {
    rest.RestConf
    UserRpc zrpc.RpcClientConf
}

添加yaml配置

etc/order.yaml

1
2
3
4
5
6
7
8
Name: order
Host: 0.0.0.0
Port: 8888
UserRpc:
  Etcd:
    Hosts:
    - 127.0.0.1:2379
    Key: user.rpc

完善服务依赖

internal/svc/servicecontext.go:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package svc

import (
    "go-zero-demo/mall/order/api/internal/config"
    "go-zero-demo/mall/user/rpc/userclient"

    "github.com/zeromicro/go-zero/zrpc"
)

type ServiceContext struct {
    Config  config.Config
    UserRpc userclient.User
}

func NewServiceContext(c config.Config) *ServiceContext {
    return &ServiceContext{
        Config:  c,
        UserRpc: userclient.NewUser(zrpc.MustNewClient(c.UserRpc)),
    }
}

添加order演示逻辑

给 getorderlogic 添加业务逻辑

internal/logic/getorderlogic.go:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package logic

import (
    "context"
    "errors"

    "go-zero-demo/mall/order/api/internal/svc"
    "go-zero-demo/mall/order/api/internal/types"
    "go-zero-demo/mall/user/rpc/types/user"

    "github.com/zeromicro/go-zero/core/logx"
)

type GetOrderLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}

func NewGetOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) GetOrderLogic {
    return GetOrderLogic{
        Logger: logx.WithContext(ctx),
        ctx:    ctx,
        svcCtx: svcCtx,
    }
}

func (l *GetOrderLogic) GetOrder(req *types.OrderReq) (*types.OrderReply, error) {
    user, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{
        Id: "1",
    })
    if err != nil {
        return nil, err
    }

    if user.Name != "test" {
        return nil, errors.New("用户不存在")
    }

    return &types.OrderReply{
        Id:   req.Id,
        Name: "test order",
    }, nil
}

启动服务并验证

启动etcd

1
$ etcd

下载依赖

1
2
# 在 go-zero-demo 目录下
$ go mod tidy

启动user rpc

1
2
3
# 在 mall/user/rpc 目录
$ go run user.go -f etc/user.yaml
Starting rpc server at 127.0.0.1:8080...

启动order api

1
2
3
# 在 mall/order/api 目录
$ go run order.go -f etc/order.yaml
Starting server at 0.0.0.0:8888...

访问order api

1
2
3
4
5
6
7
8
➜  ~ curl -i -X GET http://localhost:8888/api/order/get/1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Traceparent: 00-6754c66dfc5d4dba63c3bc6902ce1bb3-686f34deaeac4fdb-00
Date: Mon, 31 Oct 2022 15:26:22 GMT
Content-Length: 30

{"id":"1","name":"test order"}

etcd log:

1
{"level":"info","ts":"2022-10-31T23:23:09.575+0800","caller":"embed/serve.go:146","msg":"serving client traffic insecurely; this is strongly discouraged!","address":"127.0.0.1:2379"}