Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

go-zero

概述

go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。

使用 go-zero 的好处:

  • 轻松获得支撑千万日活服务的稳定性
  • 内建级联超时控制、限流、自适应熔断、自适应降载等微服务治理能力,无需配置和额外代码
  • 微服务治理中间件可无缝集成到其它现有框架使用
  • 极简的 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
# 安装 goct 软件
go install github.com/zeromicro/go-zero/tools/goctl@latest

# 安装插件
goctl env check --install --verbose --force

# 检查是否已经安装完成
goctl env check --verbose
[goctl-env]: preparing to check env

[goctl-env]: looking up "protoc"
[goctl-env]: "protoc" is installed

[goctl-env]: looking up "protoc-gen-go"
[goctl-env]: "protoc-gen-go" is installed

[goctl-env]: looking up "protoc-gen-go-grpc"
[goctl-env]: "protoc-gen-go-grpc" is installed

[goctl-env]: congratulations! your goctl environment is ready!

$ mkdir <project name> && cd <project name> # project name 为具体值
$ go mod init <module name> # module name 为具体值
$ go get -u github.com/zeromicro/go-zero@latest

DSL 语言

在 Go Zero 框架中,DSL(Domain-Specific Language)通常用于定义 API 接口、数据库模型等。Go Zero 提供了 apimodel 等工具,可以通过简洁的 DSL 来生成对应的代码。

1. API DSL

在 Go Zero 中,API 的 DSL 文件通常以 .api 结尾,定义了 HTTP 路由、请求和响应结构。下面是一个简单的 API DSL 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
info(
title: "User API"
desc: "This is a simple user API"
author: "Your Name"
version: "1.0.0"
)

type (
UserRequest {
UserId int `json:"userId"`
}

UserResponse {
Name string `json:"name"`
Email string `json:"email"`
}
)

service user-api {
@handler GetUser
get /user/:userId(UserRequest) returns (UserResponse)
}

解释:

  • info: 定义了 API 的元信息,比如标题、描述、作者、版本等。
  • type: 定义了请求和响应的结构体。
  • service: 定义了服务,user-api 是服务名。
  • @handler GetUser: 指定了处理这个请求的处理函数。
  • get /user/:userId: 定义了一个 GET 请求,路径参数为 userId,请求体使用 UserRequest,响应体使用 UserResponse

2. Model DSL

Go Zero 的 model 工具用于生成数据库模型。可以通过定义 DSL 文件,自动生成 CRUD 操作的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type (
User struct {
Id int64 `db:"id"`
Name string `db:"name"`
Email string `db:"email"`
CreatedAt time.Time `db:"created_at"`
}
)

method (
FindOne(id int64) (*User, error)
Insert(data *User) error
Update(data *User) error
Delete(id int64) error
)

解释:

  • type: 定义了数据库表结构,这里的 User 对应数据库中的 user 表。
  • method: 定义了自动生成的数据库操作方法,比如 FindOneInsertUpdateDelete

3. 如何生成代码

在定义好 .api 文件或模型的 DSL 文件后,可以使用 Go Zero 提供的工具生成对应的代码。

  • 生成 API 代码:

    1
    goctl api go -api user.api -dir .
  • 生成 Model 代码:

    1
    goctl model mysql ddl -src user.sql -dir .

通过这些 DSL,Go Zero 可以帮助开发者快速生成标准化的代码,减少手动编写的工作量。

使用gRpc方式来制作

1
2
3
4
# 单个 rpc 服务生成示例指令
$ goctl rpc protoc greet.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=. --client=true
# 多个 rpc 服务生成示例指令
$ goctl rpc protoc greet.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=. --client=true -m

model 导出数据

1
goctl model pg datasource --url "postgres://postgres:123456@127.0.0.1:5432/postgres?sslmode=disable" -table="tb_kline_config,tb_kline_data,tb_kline_rpc,tb_token_quote"

在 go-zero 框架中,通过 gRPC 的拦截器机制,可以轻松实现对每个接口调用速度的监控。拦截器是一种中间层机制,允许你在 gRPC 请求处理的前后执行额外的逻辑,例如记录日志、统计调用时间、验证权限等。

以下是实现步骤和示例代码:

实现步骤

1.    定义一个拦截器

使用 UnaryServerInterceptor 来监控单次请求,或使用 StreamServerInterceptor 来监控流式请求。

2.    在拦截器中记录每次接口调用的开始和结束时间

使用 time.Now() 捕获起始时间,计算处理完成时的耗时。

3.    在 gRPC 服务注册时添加拦截器

在服务启动时,将拦截器注册到 gRPC 服务器。

示例代码

定义拦截器

package middleware

import (
“context”
“log”
“time”

"google.golang.org/grpc"

)

// UnaryServerInterceptor for monitoring request processing time
func UnaryLoggingInterceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (resp interface{}, err error) {
start := time.Now()

// 调用实际的处理器
resp, err = handler(ctx, req)

// 计算处理时间
duration := time.Since(start)
log.Printf("Request - Method: %s, Duration: %s, Error: %v", info.FullMethod, duration, err)

return resp, err

}

// StreamServerInterceptor for monitoring stream request processing time
func StreamLoggingInterceptor(
srv interface{},
ss grpc.ServerStream,
info *grpc.StreamServerInfo,
handler grpc.StreamHandler,
) error {
start := time.Now()

// 调用实际的处理器
err := handler(srv, ss)

// 计算处理时间
duration := time.Since(start)
log.Printf("Stream Request - Method: %s, Duration: %s, Error: %v", info.FullMethod, duration, err)

return err

}

在服务启动时注册拦截器

在 go-zero 的 gRPC 服务中,拦截器可以通过配置 grpc.ServerOption 来注册。

package main

import (
“log”
“net”

"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
"your_project_path/middleware"

)

func main() {
// 创建一个 gRPC 服务器
s := zrpc.MustNewServer(zrpc.RpcServerConf{}, func(grpcServer *grpc.Server) {
// 注册服务实现(示例:注册 UserService)
RegisterUserServiceServer(grpcServer, &UserService{})
})

// 添加拦截器
s.AddUnaryInterceptors(middleware.UnaryLoggingInterceptor)
s.AddStreamInterceptors(middleware.StreamLoggingInterceptor)

// 启动服务
defer s.Stop()
log.Printf("Starting rpc server at %s...\n", s.Addr())
s.Start()

}

结果

1.    每次 gRPC 接口调用,UnaryLoggingInterceptor 或 StreamLoggingInterceptor 会自动记录接口的执行时间。
2.    日志会输出类似以下信息:

Request - Method: /UserService/GetUser, Duration: 3.2ms, Error:

3.    你可以根据 Duration 来分析接口的响应速度。

优化建议

1.    统计存储:

如果需要长时间分析接口性能,可以将数据存储到数据库或监控工具(如 Prometheus)中。

2.    结合 Metrics 工具:

使用 go-zero 提供的监控工具(如 Prometheus 集成)自动统计接口性能和调用量。

3.    区分接口类型:

根据 info.FullMethod 提取服务名和接口名,方便按模块统计。

进一步扩展

如果需要对调用速度设置阈值报警,可以在拦截器中添加逻辑:

if duration > time.Millisecond*500 { // 超过 500ms
log.Printf(“WARNING: Slow request detected - Method: %s, Duration: %s”, info.FullMethod, duration)
}

这样可以在代码中快速检测并报警慢接口问题。

引用