在当今云原生技术的浪潮中,微服务架构作为一种灵活、可扩展的架构模式,受到了越来越多企业的青睐。而在构建微服务时,选择一个高效、易用的框架至关重要。在我最近的探索中,我发现了一个令人印象深刻的工具:Go-Zero。
- Go-Zero 是什么?
Go-Zero 是一个用于构建高性能微服务的 Go 语言框架。它提供了一整套工具和组件,包括 RPC 框架、服务注册与发现、配置管理、日志记录等,旨在简化微服务的开发过程,并提供可靠的解决方案。
由国内的Tal tech团队开源,为国人点赞。
-
为什么选择 Go-Zero?
-
高性能:Go-Zero 基于 Go 语言开发,具有出色的性能和并发能力,适用于处理高并发的场景。
-
简单易用:Go-Zero 提供了清晰简洁的 API 设计,易于上手和使用。它遵循了 Go 语言的设计哲学,使得开发者能够快速构建微服务应用。
-
完整解决方案:Go-Zero 提供了一套完整的微服务开发解决方案,包括服务端和客户端的各种功能,如服务注册与发现、负载均衡、熔断器等,让开发者可以专注于业务逻辑的实
官网文档原话: 轻松获得支撑千万日活服务的稳定性 内建级联超时控制、限流、自适应熔断、自适应降载等微服务治理能力,无需配置和额外代码 微服务治理中间件可无缝集成到其它现有框架使用 极简的 API 描述,一键生成各端代码 自动校验客户端请求参数合法性 大量微服务治理和并发工具包
-
goctl
go control
goctl 是 go-zero 的内置脚手架,是提升开发效率的一大利器,可以一键生成代码、文档、部署 k8s yaml、dockerfile 等。
安装:
go install github.com/zeromicro/go-zero/tools/goctl@latest
执行goctl -version 看是否成功安装
Api
api 是 go-zero 自研的领域特性语言,不是指平常的那些接口
文件名 demo/api/user.api
syntax = "v1"
info (
title: "api demo"
desc: "演示如何编写 api 文件"
author: "Jack lee"
date: "2024 年 03 月 17 日"
version: "v1"
)
type UpdateReq {
Arg1 string `json:"arg1"`
}
type ListItem {
Value1 string `json:"value1"`
}
type LoginReq {
Username string `json:"username"`
Password string `json:"password"`
}
type LoginResp {
Name string `json:"name"`
}
type FormExampleReq {
Name string `form:"name"`
}
type PathExampleReq {
// path 标签修饰的 id 必须与请求路由中的片段对应,如
// id 在 service 语法块的请求路径上一定会有 :id 对应,见下文。
ID string `path:"id"`
}
type PathExampleResp {
Name string `json:"name"`
}
@server (
jwt: Auth // 对当前 Foo 语法块下的所有路由,开启 jwt 认证,
prefix: /v1 // 对当前 Foo 语法块下的所有路由,新增 /v1 路由前缀,
group: g1 // 对当前 Foo 语法块下的所有路由,路由归并到 g1 目录下,
timeout: 3s // 对当前 Foo 语法块下的所有路由进行超时配置
middleware: AuthInterceptor // 对当前 Foo 语法块下的所有路由添加中间件
maxBytes: 1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制,单位为 byte,goctl 版本 >= 1.5.0 才支持
)
service Foo {
// 定义没有请求体和响应体的接口,如 ping
@handler ping
get /ping
// 定义只有请求体的接口,如更新信息
@handler update
post /update (UpdateReq)
// 定义只有响应体的结构,如获取全部信息列表
@handler list
get /list returns ([]ListItem)
// 定义有结构体和响应体的接口,如登录
@handler login
post /login (LoginReq) returns (LoginResp)
// 定义表单请求
@handler formExample
post /form/example (FormExampleReq)
// 定义 path 参数
@handler pathExample
get /path/example/:id (PathExampleReq) returns (PathExampleResp)
}
在api目录里执行一下
goctl api go -api user.api -dir .
第一个api指的是根据api类型的生成代码,
go指 生成go的代码(支持dart、ts、kt(kotlin))
-api 指用哪个api
-dir . 生成的代码放到当前目录下
生成后的代码结构
.
└── api
├── etc
│ └── foo.yaml
├── foo.go
├── internal
│ ├── config
│ │ └── config.go
│ ├── handler
│ │ ├── g1
│ │ │ ├── formexamplehandler.go
│ │ │ ├── listhandler.go
│ │ │ ├── loginhandler.go
│ │ │ ├── pathexamplehandler.go
│ │ │ ├── pinghandler.go
│ │ │ └── updatehandler.go
│ │ └── routes.go
│ ├── logic
│ │ └── g1
│ │ ├── formexamplelogic.go
│ │ ├── listlogic.go
│ │ ├── loginlogic.go
│ │ ├── pathexamplelogic.go
│ │ ├── pinglogic.go
│ │ └── updatelogic.go
│ ├── middleware
│ │ └── authinterceptormiddleware.go
│ ├── svc
│ │ └── servicecontext.go
│ └── types
│ └── types.go
└── user.api
proto 语法
Protocol buffers 是 Google 的语言中立、平台中立、可扩展的结构化数据序列化机制——像 XML,但更小、更快、更简单。您定义了一次数据的结构化方式,然后您可以使用特殊生成的源代码轻松地将结构化数据写入各种数据流并使用各种语言从中读取结构化数据(说人话就是,用pb语法写的代码,可以生成不同的语言的版本)
来写个简单的
文件名:user/rpc/user.proto
syntax = "proto3";
package user;
option go_package = "./user";
message IdRequest{
string id = 1;
}
message UserResponse{
string id = 1;
string name = 2;
bool gender = 3;
}
service User{
rpc getUser(IdRequest) returns(UserResponse);
}
执行命令:
goctl rpc protoc user/rpc/user.proto --go_out=user/rpc/types --go-grpc_out=user/rpc/types --zrpc_out=user/rpc
生成的代码结构
└── rpc
├── etc
│ └── user.yaml
├── internal
│ ├── config
│ │ └── config.go
│ ├── logic
│ │ └── getuserlogic.go
│ ├── server
│ │ └── userserver.go
│ └── svc
│ └── servicecontext.go
├── types
│ └── user
│ ├── user.pb.go
│ └── user_grpc.pb.go
├── user.go
├── user.proto
└── userclient
└── user.go
接着在
user/rpc/user.yaml补充etcd的内容
Name: user.rpc
ListenOn: 127.0.0.1:8080
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc
etcd 下载
下载压缩包,最新版本
wget https://github.com/etcd-io/etcd/releases/download/v3.5.12/etcd-v3.5.12-darwin-amd64.zip
解压etcd
unzip etcd-v3.5.12-darwin-amd64.zip
进入etcd 文件夹
cd etcd-v3.5.12-darwin-amd64
执行命令:
./etcd -version
启动etcd
./etcd &
简单的etcd命令
使用命令
./etcdctl put user.rpc 127.0.0.1:2379(只用这个就行)
./etcdctl delete user.rpc 删除
配置微服务
在/user/rpc/internal/logic/getuserlogic.go 中的GetUser方法中
添加
return &user.UserResponse{
Id: "1",
Name: "Jack",
Gender: true,
}, nil
1 在user/rpc 中执行, go run user.go 启动user的服务
2 在apipost中,新建->新建grpc->导入proto->本地导入,把user.proto文件上传。
然后输入服务器地址:127.0.0.1:8080,点击调用
就能看到返回了
- 接着配置 video的服务,在这里调用user的服务
新建一个文件video/api/video.api
syntax = "v1"
info (
author: "jack"
date: "2023年3月17日"
desc: "first video in go-zero"
)
type (
VideoReq {
Id string `path:"id"`
}
VideoRes {
Id string `json:"id"`
Name int64 `json:"name"`
}
)
service video {
@handler getVideo
get /api/videos/:id (VideoReq) returns (VideoReq)
}
执行命令
cd video/api/
goctl api go -api video.api -dir .(注意有个点,代表当前目录)
修改配置
在video/api/etc/video.yml中增加刚才的user的配置
UserRpc:
Etcd:
hosts:
- 127.0.0.1:2379
Key: user.rpc
1 在 video/api/internal/servicecontext.go中
type ServiceContext struct {
Config config.Config
UserRpc userclient.User # 增加user服务的信息
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
UserRpc: userclient.NewUser(zrpc.MustNewClient(c.UserRpc)),# 增加user服务的信息
}
}
在video/api/internal/handler/getvideohandler.go的getvideo中
增加代码
func (l *GetVideoLogic) GetVideo(req *types.VideoReq) (resp *types.VideoRes, err error) {
// todo: add your logic here and delete this line
user1, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{
Id: "1",
})
if err != nil {
return nil, err
}
return &types.VideoRes{Id: req.Id, Name: user1.Name}, nil
}
然后再video/api中执行 go run video.go 启动video 服务端口号 8888
这样在浏览器里访问刚才生成的路由
127.0.0.1:8888/api/videos/1
返回代码
{
"id": "1",
"name": "Jack"
}
至此通过请求video的id,获取video的user结束。