入门

1. 环境准备

  • go版本:
C:\Users\Huo>go version
go version go1.20.1 windows/amd64
1
2
  • goctl安装:

    goctl(官方建议读go control)是go-zero微服务框架下的代码生成工具。使用 goctl 可显著提升开发效率,让开发人员将时间重点放在业务开发上,其功能有:

    • api服务生成
    • rpc服务生成
    • model代码生成
    • 模板管理
    go install github.com/zeromicro/go-zero/tools/goctl@v1.4.4
    
    1

    这时候会在gopath的bin目录下生成goctl的执行进程(注意要将%GOPATH%\bin设置到path环境变量中)。

    C:\Users\Huo>goctl -version
    goctl version 1.4.4 windows/amd64
    
    1
    2
  • protoc & protoc-gen-go安装

    go-zero提供了便捷的安装方式:

    goctl env check -i -f --verbose
    
    1

IDE工具我这里选择的是Goland,原因是我喜欢用Jetbrains的全家桶。

大家也可以使用广受好评的VS Code,免费的IDE,非常强大。

2.HelloWorld

接下来我们就写一个经典的Hello World程序进行一个简单的入门。

D:\go\project\gozero>mkdir helloworld
D:\go\project\gozero>cd helloworld
D:\go\project\gozero\helloworld>goctl api new hello
D:\go\project\gozero\helloworld>cd hello
D:\go\project\gozero\helloworld\hello>go mod tidy
1
2
3
4
5

使用goland打开,编写程序。

image-20230222221623254

我们改一下路由,将自动生成的form/:name 改为hello

func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
	server.AddRoutes(
		[]rest.Route{
			{
				Method:  http.MethodGet,
				Path:    "/hello",
				Handler: HelloHandler(serverCtx),
			},
		},
	)
}
1
2
3
4
5
6
7
8
9
10
11
type Request struct {
	//Name string `path:"name,options=you|me"`
}
1
2
3
func (l *HelloLogic) Hello(req *types.Request) (resp *types.Response, err error) {
	// todo: add your logic here and delete this line
	resp = &types.Response{
		Message: "Hello go-zero",
	}
	return
}

1
2
3
4
5
6
7
8

启动程序,访问http://localhost:8888/hello

// http://localhost:8888/hello

{
  "message": "Hello go-zero"
}
1
2
3
4
5

ok,入门了~

3. 微服务版HelloWorld

场景:

假设有两个服务:

  • order service
  • user service

用户在查询订单时,同时需要获取用户信息。

D:\go\project\gozero>cd microhelloworld
D:\go\project\gozero\microhelloworld>mkdir mall
D:\go\project\gozero\microhelloworld>cd mall
D:\go\project\gozero\microhelloworld\mall>goctl api new order
D:\go\project\gozero\microhelloworld\mall>goctl api new user
D:\go\project\gozero\microhelloworld\mall>go work init
D:\go\project\gozero\microhelloworld\mall>go work use order
D:\go\project\gozero\microhelloworld\mall>go work use user
1
2
3
4
5
6
7
8

我们先在user模块提供一个api,用于order调用。

user/rpc/user.proto

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
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

前面我们讲过,服务和服务之间一般使用rpc通信,最常使用的就是grpc,接下来就是使用之前安装的protoc-gen-go将上述的proto文件生成go代码。

这里我们使用的go-zero框架,仍旧是go-zero的方式:

D:\go\project\gozero\microhelloworld\mall\user\rpc> goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=.
1
D:\go\project\gozero\microhelloworld\mall\user\rpc> cd ..
D:\go\project\gozero\microhelloworld\mall\user> go mod tidy
1
2

在logic中填充业务逻辑:

func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) {
	// todo: add your logic here and delete this line

	return &user.UserResponse{
		Id:   "1",
		Name: "test",
	}, nil
}
1
2
3
4
5
6
7
8

接下来在order模块,写一个接口,获取订单信息,其中订单信息中需要携带用户信息。

编写order.api

type Request {
	Name string `path:"name,options=you|me"`
}

type Response {
	Message string `json:"message"`
}

type(
	OrderReq {
		Id string `path:"id"`
	}

	OrderReply {
		Id string `json:"id"`
		Name string `json:"name"`
		UserName string `json:"userName"`
	}
)

service order-api {
	@handler OrderHandler
	get /from/:name(Request) returns (Response)
	@handler GetOrder
    get /api/order/get/:id (OrderReq) returns (OrderReply)
}
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

生成order服务

goctl api go -api order.api -dir ./gen
1

添加user rpc配置:

type Config struct {
    rest.RestConf
    UserRpc zrpc.RpcClientConf
}
1
2
3
4

在order-api.yaml

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

创建对user服务的rpc调用

package svc

import (
	"github.com/zeromicro/go-zero/zrpc"
	"order/internal/config"
	"user/userclient"
)

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)),
	}
}

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

编写逻辑

package logic

import (
	"context"
	"errors"
	"order/internal/types"
	"user/types/user"

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

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) (resp *types.OrderReply, err error) {
	// todo: add your logic here and delete this line
	userId := l.getOrderById(req.Id)
	uResp, err := l.svcCtx.UserRpc.GetUser(context.Background(), &user.IdRequest{
		Id: userId,
	})
	if err != nil {
		return nil, err
	}
	if uResp.Name != "test" {
		return nil, errors.New("用户不存在")
	}
	return &types.OrderReply{
		Id:       req.Id,
		UserName: uResp.Name,
		Name:     "test order",
	}, nil
}

func (l *GetOrderLogic) getOrderById(id string) string {
	return "1"
}

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
45
46
47
48
49

由于依赖etcd,所以需要启动一个etcd

我们使用docker启动

在mall目录下编写docker-compose.yml

version: '3'
services:
  Etcd:
    container_name: etcd3
    image: bitnami/etcd:${ETCD_VERSION}
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
    privileged: true
    volumes:
      - ${ETCD_DIR}/data:/bitnami/etcd/data
    ports:
      - ${ETCD_PORT}:2379
      - 2380:2380
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

同目录下创建.env

COMPOSE_PROJECT_NAME=gozero-demo-mall
ETCD_DIR=D:/go/project/gozero/etcd
ETCD_VERSION=3.5.6
ETCD_PORT=2379
1
2
3
4
docker-compose up -d
1

分别启动user服务和order服务。

// http://localhost:8888/api/order/get/1

{
  "id": "1",
  "name": "test order",
  "userName": "test"
}
1
2
3
4
5
6
7

成功~

恭喜你,入门了~~