本文主要讲一下如何基于Go-Zero写一个简易的微服务案例,并结合ETCD进行部署,为了方便,直接把客户端和服务端写在同一个项目中。
项目分为2块,service是微服务提供两个方法,分别是获取token和解析token;client是客户端,封装了API,接收参数去调用微服务提供的两个方法,总体目录如下:
1
2
3
4
5
6
7
|
.
├── client # 存放API相关代码,封装为API,内部调用微服务返回数据
├── define # 配置内容
├── go.mod # mod依赖
├── go.sum
├── service # 存放微服务相关代码
└── utils # 一些工具包,包括格式化返回数据,token的加解密
|
提前准备
创建ETCD容器并启动
为了方便这里直接用Docker来创建ETCD容器,镜像用的是bitnami/etcd。
1
|
docker run -d --name etcd-server --network app-tier --publish 2379:2379 --publish 2380:2380 --env ALLOW_NONE_AUTHENTICATION=yes --env ETCD_ADVERTISE_CLIENT_URLS=http://etcd-server:2379 bitnami/etcd:latest
|
安装goctl
goctl 的最早功能是为了解决 GRPC 内网调试问题,后面逐渐成为一个代码生成脚手架。goctl 的代码生成覆盖了Go、Java、Android、iOS、TS 等多门语言,通过 goctl 可以一键生成各端代码,让开发者将精力集中在业务开发上。
Goctl 安装有两种方式,分别是 go get 或者 go install,其次是 docker。本人电脑是Mac,直接brew进行安装。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# Go 1.16 之前版本
$ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest
# Go 1.16 及以后版本
$ GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest
# macOS
$ brew install goctl
# 验证
$ goctl --version
goctl version 1.3.5 darwin/amd64
# 查看 goctl
$ cd $GOPATH/bin && ls | grep "goctl"
|
创建一个项目
1
|
mkdir go-zero-microservice-demo
|
初始化mod
1
2
|
cd go-zero-microservice-demo
go mod init
|
关于go mod
go mod是目前比较流行的包管理工具,需要go版本1.11以上,开启配置如下
1
2
3
|
go env -w GOBIN=/usr/local/go/bin #配置下go bin
go env -w GO111MODULE=on #开启go mod
go env -w GOPROXY=https://goproxy.cn,direct #七牛云代理
|
创建服务端
创建user服务
1
2
3
4
|
# 此时在service文件夹下
mkdir user
cd user
vim user.proto
|
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
24
25
26
27
28
29
30
31
32
33
34
35
36
|
syntax = "proto3";
package user;
option go_package="./service"; //注意这个参数要跟包名一致
message CommonResponse{
string code = 1;
string msg = 2;
string data = 3;
}
message GetTokenRequest{
int32 userId = 1;
string groupId = 2;
string userName = 3;
}
message GetTokenResponse{
string token = 1;
}
message AuthRequest{
string token = 1;
}
message AuthResponse{
int32 userId = 1;
string groupId = 2;
string userName = 3;
string expireTime = 4;
}
service User {
rpc GetToken(GetTokenRequest) returns (GetTokenResponse);
rpc JwtAuth(AuthRequest) returns (AuthResponse);
}
|
生成代码
1
|
goctl rpc protoc user.proto --go_out=./core --go-grpc_out=./core --zrpc_out=. -style go-zero
|
此时service目录结构如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
.
├── core
│ └── service # RPC代码桩内容,用于给客户端调用方法
│ ├── user.pb.go
│ └── user_grpc.pb.go
├── etc
│ └── user.yaml # 配置文件,启动时需要添加ETCD相关参数
├── internal
│ ├── config
│ │ └── config.go
│ ├── logic
│ │ ├── get-token-logic.go # 获取token服务的代码
│ │ └── jwt-auth-logic.go # 解析token服务的代码
│ ├── server
│ │ └── user-server.go
│ └── svc
│ └── service-context.go
├── user
│ └── user.go # 客户端文件,使用时调用里面的newuser方法进行初始化
├── user.go
└── user.proto
|
编写服务内容
get-token-logic.go
1
2
3
4
5
6
7
8
9
10
|
func (l *GetTokenLogic) GetToken(in *service.GetTokenRequest) (*service.GetTokenResponse, error) {
token, err := common.GenerateToken(int(in.UserId), in.UserName, in.GroupId)
if err != nil {
return nil, err
}
return &service.GetTokenResponse{
Token: token,
}, nil
}
|
jwt-auth-logic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
|
func (l *JwtAuthLogic) JwtAuth(in *service.AuthRequest) (*service.AuthResponse, error) {
uc, err := common.AnalyzeToken(in.Token)
if err != nil {
return nil, err
}
return &user.AuthResponse{
UserId: int32(uc.Id),
GroupId: uc.GroupId,
UserName: uc.Name,
ExpireTime: "2023-01-01 00:00:00",
}, nil
}
|
修改启动配置文件
1
2
3
4
5
6
7
|
Name: user.rpc
ListenOn: 0.0.0.0:8080
#Mode: dev
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc # 这个key后续要用于客户端
|
启动服务
1
2
|
# 此时在service文件夹下
go run user.go -f ./etc/user.yaml
|
创建客户端,并封装API对外提供服务
创建API服务
1
2
3
|
cd client
goctl api new core
cd core
|
编写API内容
core.api
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
|
type GetTokenRequest {
UserId int `json:"user_id"`
UserName string `json:"user_name"`
GroupId string `json:"group_id"`
}
type GetTokenResponse {
Token string `json:"token"`
}
type UserInfoRequest {
Token string `json:"token"`
}
type UserInfoResponse {
UserName string `json:"user_ame"`
GroupId string `json:"group_id"`
}
service core-api {
@handler GettokenHandler
post /token/gettoken(GetTokenRequest) returns (GetTokenResponse)
@handler UserInfoHandler
post /user/userinfo(UserInfoRequest) returns (UserInfoResponse)
}
|
生成API代码
1
|
goctl api go -api core.api -dir . -style go_zero
|
此时core目录结构如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
.
├── core.api # API模板
├── core.go # 服务启动入口
├── etc
│ └── core-api.yaml # 启动的配置信息
└── internal
├── config
│ └── config.go # 配置文件
├── handler # 存放生成的API路由代码
│ ├── gettoken_handler.go
│ ├── routes.go
│ └── user_info_handler.go
├── logic # 存放实际的业务代码
│ ├── gettoken_logic.go
│ └── user_info_logic.go
├── middleware
├── svc
│ └── service_context.go # svc对象,使用微服务需要在这里初始化
└── types
└── types.go
|
微服务客户端初始化
修改service_context.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package svc
import (
"github.com/zeromicro/go-zero/zrpc"
"go-zero-microservice-demo/client/core/internal/config"
"go-zero-microservice-demo/service/user" // 切记这里是上面提到的客户端
)
type ServiceContext struct {
Config config.Config
UserRpc user.User
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)),
}
}
|
添加业务逻辑代码
gettoken_logic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
|
func (l *GettokenLogic) Gettoken(req *types.GetTokenRequest) (resp *types.GetTokenResponse, err error) {
uc, err := l.svcCtx.UserRpc.GetToken(l.ctx, &user.GetTokenRequest{
UserId: int32(req.UserId),
GroupId: req.GroupId,
UserName: req.UserName,
})
if err != nil {
return nil, err
}
resp = &types.GetTokenResponse{}
resp.Token = uc.Token
return
}
|
user_info_logic.go
1
2
3
4
5
6
7
8
9
10
11
12
|
func (l *UserInfoLogic) UserInfo(req *types.UserInfoRequest) (resp *types.UserInfoResponse, err error) {
uc, err := l.svcCtx.UserRpc.JwtAuth(l.ctx, &user.AuthRequest{
Token: req.Token,
})
if err != nil {
return nil, err
}
resp = &types.UserInfoResponse{}
resp.UserName = uc.UserName
resp.GroupId = uc.GroupId
return
}
|
修改启动服务文件
core-api.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
Name: core-api
Host: 0.0.0.0
Port: 8888
# 日志配置
Log:
Mode: file
Path: ./demo-logs
KeepDays: 15
MaxSize: 10
Rotation: daily
UserRpc:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc
# 弱依赖,连接不上不会panic
NonBlock: true
|
启动服务
1
2
|
# 此时在core文件夹下
go run core.go -f ./etc/core-api.yaml
|
调用截图
获取token
解析token
demo下载地址
https://github.com/kayoon/go-zero-microservice-demo
注意事项
- 如果缺少相关依赖包,可以在项目根目录下用
go mod tidy
进行补充