快速开发-微服务
由于go-zero的中文文档与最新版本的go-zero的一些部分有冲突,在磕磕绊绊踩了两天坑后终于算是跑起来了快速入门的例子。下面是我在学习过程中对文档不正确的地方修改后的笔记。
开发准备
配置Go Module
Go Module是Golang管理依赖性的方式
这些应该在刚开始go的时候就已经配好了。
查看GO111MODULE开启情况:
go env GO111MODULE
如果为on,则跳过下一步。否则go env -w GO111MODULE="on"
设置GOPROXY:
go env -w GOPROXY=goproxy.cn
设置GOMODCACHE:
go env GOMODCACHE 如果目录不为空或者/dev/null,跳过下一步
go env -w GOMODCACHE=$GOPATH/pkg/mod
安装go-zero:
go get -u github.com/zeromicro/go-zero
安装goctl工具:
goctl是go-zero微服务框架下的代码生成工具。
go install github.com/zeromicro/go-zero/tools/goctl@latest
能不用go get就不用吧,go get下载的goctl可能会出现"goctl : 无法将“goctl”项识别为 cmdlet、函数.."的问题。
安装protoc&protoc-gen-go:
为了实现rpc通信。
protoc是一款用C++编写的工具,其可以将proto文件翻译为指定语言的代码。在go-zero的微服务中,我们采用grpc进行服务间的通信,而grpc的编写就需要用到protoc和翻译成go语言rpc stub代码的插件protoc-gen-go。
goctl env check -i -f --verbose
congratulations! your goctl environment is ready! 说明全部下载成功。
下载失败请尝试挂梯子。
选择性环境
前面的是开发阶段必须要准备的环境。后面的环境可以选择性安装,因为他们一般存在于服务器。
etcd,redis,mysql
api配置
Config
type Config struct{
rest.RestConf // rest api配置
Auth struct { // jwt鉴权配置
AccessSecret string // jwt密钥
AccessExpire int64 // 有效期,单位:秒
}
Mysql struct { // 数据库配置,除mysql外,可能还有mongo等其他数据库
DataSource string // mysql链接地址,满足 $user:$password@tcp($ip:$port)/$db?$queries 格式即可
}
CacheRedis cache.CacheConf // redis缓存
UserRpc zrpc.RpcClientConf // rpc client配置
}
rest.RestConf
api服务基础配置,包含监听地址,监听端口,证书配置,限流,熔断参数,超时参数等控制。
rpc配置
rpc配置控制着一个rpc服务的各种功能,包含但不限于监听地址,etcd配置,超时,熔断配置等。
快速开始 --微服务
单体服务与微服务区别在于有没有服务间通信。
一个订单服务,一个用户服务
订单是直接面向用户,通过http协议访问数据,而订单内部需要获取用户的一些基础数据,既然我们的服务是采用微服务的架构设计, 那么两个服务(user, order)就必须要进行数据交换,服务间的数据交换即服务间的通讯,到了这里,采用合理的通讯协议也是一个开发人员需要 考虑的事情,可以通过http,rpc等方式来进行通讯,这里我们选择rpc来实现服务间的通讯。
从上文得知,我们需要一个
- user rpc
- order api
创建user rpc服务
在项目根目录下创建user/rpc
添加user.proto文件,增加getUser方法
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);
}
写好user.proto文件后,在同级目录下(user/rpc)
执行生成代码的指令:
goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=.
如图中的getuserlogic.go就是根据proto自动生成的代码,需要在GetUser部分添加业务逻辑代码。
注意! 在生成代码后,可能需要手动修改etc/user.yaml文件中的部分内容。将在后续提到。 (这是与文档不一致的地方之一)
创建order api服务
在douyinbackend/douyinbackend目录下,创建order文件夹,并创建api子文件夹。api子文件夹中创建order.api:
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服务
goctl api go -api order.api -dir .
添加user rpc配置,注意,这一步仍然是在order目录下进行的
在上面生成的order下的internal/config/config.go中:
package config
import (
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/zrpc"
)
type Config struct {
rest.RestConf
UserRpc zrpc.RpcClientConf
}
添加yaml配置:
在order目录下的etc/order.yaml中,写入:
Name: order
Host: 0.0.0.0
Port: 8081
UserRpc:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc
要注意第七行 - 与 ip地址之间有一空格!!否则在启动order时,会报错:
2023/01/24 23:57:07 error: config file etc/order.yaml, string: `-127.0.0.1:2379`, error:
`json: cannot unmarshal number into Go value of type []interface {}`
exit status 1
注意! 这里的Host与Port就是rpc服务的host和端口号,一定要保持一致!
完善服务依赖: 在order目录下的internal/svc/servicecontext.go中:
其中第6行,第11行需要手动添加 (好像) 注意! 这里导入的是user/rpc/userclient,而不是中文文档中的user/rpc/user
package svc
import (
"douyinbackend/douyinbackend/order/api/internal/config"
"douyinbackend/douyinbackend/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业务逻辑,同样在order目录下的internal/logic/getorderlogic.go.
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
windows下etcd下载:
选择etcd-v3.5.7-windows-amd64.zip
下载解压后,添加到系统环境变量:在高级系统设置中打开编辑环境变量的窗口,找到Path,新建,添加解压后的地址。如:D:\etcd-v3.5.7-windows-amd64\。 记得添加后所有窗口全部确认。使用etcd -version 检查是否成功。
终端 etcd 启动etcd
在douyinbackend目录下(项目根目录) 下载依赖: go mod tidy
启动user rpc:
在user/rpc目录下, go run user.go -f etc/user.yaml
启动order api:
在order/api目录下,go run order.go -f etc/order.yaml
如果启动etcd有问题,检查是否加入到了环境变量中。
如果启动user rpc有问题,检查端口是否被占用。示例中的端口为8080,可以在命令行中使用netstat -aon|findstr "8080",检查是否被占用。
如果被占用,可以在user/rpc/etc/user.yaml中,修改ListeOn如下:
修改后重新开启rpc服务。
如果启动order api有问题,如果报错:
`json: cannot unmarshal number into Go value of type []interface {}`
exit status 1
则检查order的order.yaml文件中 - 是否与Ip之间有空格。
如果报错:
error: context deadline exceeded, make sure rpc service "add.rpc" is alread started
则表示没有找到rpc服务,检查order.yaml的Host和Port是否与user.yaml的
ListenOn一致。
访问order api:
可以使用接口测试工具调试。
推荐文档:go-zero.dev/cn/docs 这个要新一点点