go-zero: https://github.com/zeromicro/go-zero
官方文档:https://go-zero.dev/docs/tutorials
1 关于goctl工具
官方文档中有句话触动到我了:工具大于约定和文档。
触动2:不要去控制代码,而是去控制它。(今日份触动值达标~)
goctl工具提供了一键生成api(http)项目与rpc项目的方式,只需编写资源描述文件.proto、.api,即可省去1小时手动搭建服务框架的时间~
还支持model层代码生成,构建docker镜像,编写k8s yaml文件… 就。很强大
安装goctl:
GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest
2 生成http服务与rpc服务
2.1 生成http服务
以下命令也利用goland安装一个goctl插件,全部都能交互式操作哦~
先创建一个.api模板,定义自己的服务与接口:
goctl api -o tpl/user.api
文件内容:
syntax = "v1"
info (
title: "My User Service"
desc: "My User Service"
author: "xxx"
email: "xxx@xxx.com"
version: "1.0"
)
type GetUserRequest {
id string
}
type GetUserResponse {
name string
email string
}
type LoginRequest {
id string
password string
}
type LoginResponse {}
//@server(
// jwt: Auth // 开启jwt认证
//)
service user {
@doc "根据id查询用户信息"
@handler GetUser
get /user/:id (GetUserRequest) returns (GetUserResponse)
@doc "用户登录"
@handler Login
post /user/login (LoginRequest) returns (LoginResponse)
}
定义好.api模板后,就可以一键生成自己的http服务了:
goctl api go -api tpl/user.api -dir ./cmd/api/user -style gozero
可以看到goctl帮我们生成了完成的服务框架:
├── etc
│ └── user.yaml #配置文件,根据服务需要修改,比如添加mysql配置、redis配置
├── internal
│ ├── config #对应yaml配置
│ │ └── config.go
│ ├── handler #http服务的路由
│ │ ├── getuserhandler.go
│ │ ├── loginhandler.go
│ │ └── routes.go
│ ├── logic #业务接口逻辑,需要自己补充
│ │ ├── getuserlogic.go
│ │ └── loginlogic.go
│ ├── svc
│ │ └── servicecontext.go #上下文
│ └── types
│ └── types.go
└── user.go #main入口
2.2 生成rpc服务
同样的步骤,要先创建一个.proto模板,定义好自己的服务和接口:
goctl rpc -o tpl/user.proto
.proto内容:
syntax = "proto3";
package user;
option go_package="./user";
message GetUserRequest {
string id = 1;
}
message GetUserResponse {
string name = 1;
string email = 2;
}
message LoginRequest {
string id = 1;
string password = 2;
}
// todo: 不支持引用第三方google.protobuf.Empty
message LoginResponse {
}
service User {
rpc GetUser(GetUserRequest) returns(GetUserResponse);
rpc Login(LoginRequest) returns(LoginResponse);
}
一键生成rpc服务框架结构:
goctl rpc protoc tpl/user.proto --go_out=./cmd/rpc/user --go-grpc_out=./cmd/rpc/user --zrpc_out=./cmd/rpc/user
rpc生成后的目录结构:
├── etc
│ └── user.yaml #配置文件,根据服务需要修改,比如添加mysql配置、redis配置
├── internal
│ ├── config #对应yaml配置
│ │ └── config.go
│ ├── logic #业务逻辑,需要自己补充
│ │ ├── getuserlogic.go
│ │ └── loginlogic.go
│ ├── server #grpc server接口实现,直接调用了logic
│ │ └── userserver.go
│ └── svc
│ └── servicecontext.go #上下文
├── user
│ ├── user_grpc.pb.go
│ └── user.pb.go
├── userclient
│ └── user.go
└── user.go
2.3 http与rpc的统一服务
使用上述方式可以分别生成http服务与rpc服务,那么我们如果想把http与rpc封在一个服务中,要怎么做呢? 将上述过程全部实现一遍吗?
goctl并不支持生成一个包含api和rpc服务的单一main入口的方式,但goctl提供了一种gateway支持,可以将定义好的rpc服务转换为api服务,也就是说,我们仅需定义rpc接口,然后使用gateway方式生成api.
完整流程:
(1)使用goctl生成rpc服务的代码
(2)创建配置文件 gateway.yaml,如下:
Name: hello-gateway
Host: localhost
Port: 8000 #api端口
Upstreams:
- Grpc:
Target: localhost:9000 #rpc端口, 与rpc中定义的一致
Mappings:
- Method: get
Path: /ping
RpcPath: hello.Hello/Ping #api接口对应的rpc接口,格式{serviceName}.{method}
(3)改造rpc服务,将入口main函数改为服务启动函数,接收调用
package hello
import (
//...
)
func RunRpcServer(configFile string) {
var c config.Config
conf.MustLoad(configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
hello.RegisterHelloServer(grpcServer, server.NewHelloServer(ctx))
reflection.Register(grpcServer)
})
defer s.Stop()
s.Start()
}
(4)编写一个新的main.go入口文件,初始化这两个服务
package main
import (
"flag"
"fmt"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/gateway"
"test/go-zero/cmd/rpc/hello"
"time"
)
var (
rpcConfigFile = flag.String("rpc_conf", "rpc/hello/etc/config.yaml", "rpc config file")
gatewayConfigFile = flag.String("gw_conf", "etc/gateway.yaml", "gateway config file")
)
func main() {
flag.Parse()
// 开启rpc
go hello.RunRpcServer(*rpcConfigFile)
time.Sleep(time.Millisecond * 100)
// 开启api
var c gateway.GatewayConf
conf.MustLoad(*gatewayConfigFile, &c)
gw := gateway.MustNewServer(c)
defer gw.Stop()
go gw.Start()
fmt.Printf("Start rpc server at %s\n", c.Upstreams[0].Grpc.Target)
fmt.Printf("Start http server at %+v:%+v\n", c.Host, c.Port)
select {}
}
这样就成功打造了一个同时提供api和rcp两种方式的服务。
nice~ 开始编写自己的服务吧~
3 生成model层代码
goctl支持基于ddl生成model层代码,还可以选择不带redis cache、带redis cache生成。
目前支持mysql、pg、mongo三种数据库:
Usage:
goctl model [command]
Available Commands:
mongo Generate mongo model
mysql Generate mysql model
pg Generate postgresql model
3.1 生成mysql的model层代码
先创建.sql文件,定义库表结构:
CREATE TABLE `user` (
`id` char(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'ID',
`name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名',
`email` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮箱',
`password` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密钥',
`create_t` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
然后就可以根据.sql一键生成model层代码:
goctl model mysql ddl -src=./tpl/user.sql -dir=./model/sql
可以看到生成了model层结构为:
model/sql/
├── usermodel_gen.go #封装的db的连接操作、CRUD操作
├── usermodel.go #model层入口
└── vars.go
若要开启带缓存模式,添加-cache参数即可:
goctl model mysql ddl -src=./tpl/user.sql -dir=./model/sql -cache
那么生成的model层入口中,是增加了cache.CacheConf
这个redis cache配置参数的:
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserModel
可以利用这个做一些db与cache的组合操作。
3.2 生成pg的model层代码
除了上述方式根据ddl生成model层代码,还可以基于datasource生成,比如根据pg url生成:
goctl model pg datasource --url="postgres://user:123@localhost:5432/testDB?sslmode=disable" --table="*"
还没试过
3.3 生成mongo的model层代码
这个就不需要编写模板文件了,直接命令生成mongo的model层代码:
goctl model mongo --type User --dir ./model/mongo --cache
生成的结构:
model/mongo/
├── error.go
├── usermodelgen.go #db操作,CRUD
├── usermodel.go #model层入口
└── usertypes.go #字段定义
mongo爱好者狂喜==