
專案設計
說明
專案是透過 zo-zero 框架實現一個簡單的使用者訂單系統,包括兩個微服務使用者服務和訂單服務,透過 API 介面能夠分別呼叫兩個服務的介面,使用者服務暴露 RPC 服務介面供訂單服務進行呼叫。下面截圖是我的操作過程


API
goctl指令
goctl api go -api user.api -dir . -style goZero
goctl api go -api order.api -dir . -style goZero
User
syntax = "v1"
// 进行项目的简单说明
info (
title: "实现简单的用户服务"
author: "aorangehc"
date: "2025年 11 月 11 日"
version: "v1"
)
// 注册需要用户名、密码、手机号码,要做唯一性检测
type RegisterReq {
UserName string `json:"username"`
Password string `json:"password"`
Repassword string `json:"re_password"`
Mobile string `json:"mobile"`
Gender int `json:"gender,options=0|1|2,default=0"` // male|female|unknown
NickName string `json:"nickname"`
}
type RegisterResp {
success bool `json:"success"` // 注册是否成功
Message string `json:"message"` // 注册报错信息
Id int64 `json:"id"` // 成功注册返回的 id
}
type LoginReq {
UserName string `json:"username"`
Password string `json:"password"`
}
type LoginResp {
Message string `json:"message"`
AccessToken string `json:"accessToken"`
AccessExpire int64 `json:"accessExpire"`
RefreshAfter int64 `json:"refreshAfter"`
}
type UserInfoReq {
Id int64 `json:"id"`
}
type UserInfoResp {
Id int64 `json:"id"`
Token string `json:"token"`
UserName string `json:"username"`
Mobile string `json:"mobile"`
Gender int `json:"gender,options=0|1|2,default=0"` // male|female|unknown
NickName string `json:"nickname"`
CreateAt string `json:"createAt"` // 注册时间
UpdateAt string `json:"updateAt"` // 最后更新时间
}
// 用户信息更新
type UserUpdateReq {
Id int64 `json:"id"`
UserName string `json:"username"`
Password string `json:"password"`
Mobile string `json:"mobile"`
Gender int `json:"gender,options=0|1|2,default=0"` // male|female|unknown
NickName string `json:"nickname"`
}
type UserUpdateResp {
success bool `json:"success"`
Message string `json:"message"` // 更新报错信息
}
// 定义用户服务的 Api
@server (
prefix: api
)
service user-api {
@handler register
post /user/register (RegisterReq) returns (RegisterResp)
@handler login
post /user/login (LoginReq) returns (LoginResp)
}
@server (
prefix: api
jwt: Auth
)
service user-api {
@handler userInfo // 获取用户信息
get /user/:id (UserInfoReq) returns (UserInfoResp)
}
Order
syntax = "v1"
info (
title: "订单服务简单 demo"
author: "aorangehc"
date: "2025年 11 月 11 日"
version: "v1"
)
// 创建订单
type CreateReq {
UserId int64 `json:"userId"`
GoodsId int64 `json:"goodsId"`
Num int64 `json:"num"`
Amount int64 `json:"amount"`
}
type CreateResp {
OrderSn string `json:"orderSn"`
}
// 查看订单详情
type DetailReq {
OrderSn string `json:"orderSn"`
}
type DetailResp {
OrderSn string `json:"orderSn"`
UserId int64 `json:"userId"`
GoodsId int64 `json:"goodsid"`
Num int64 `json:"num"`
Amount int64 `json:"amount"`
Status int64 `json:"status"`
CreateAt string `json:"createAt"`
}
// 删除订单
type DeleteReq {
userId int64 `json:"userId"`
OrderSn string `json:"orderSn"`
}
type DeleteResp {
success bool `json:"success"`
Message string `json:"message"`
}
service order-api {
@handler create
post /api/order/create (CreateReq) returns (CreateResp)
@handler detail
get /api/order/:orderSn (DetailReq) returns (DetailResp)
@handler delete
delete /api/order/delete (DeleteReq) returns (DeleteResp)
}
RPC
goctl指令
goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=.
User
// 暴露一个 user 服务对外接口,获取用户信息
syntax = "proto3";
option go_package = "./user";
// 定义请求结构体
message GetUserInfoRequest {
int64 user_id = 1; // 用户ID
}
// 定义响应结构体
message GetUserInfoResponse {
int64 user_id = 1; // 用户ID
string user_name = 2; // 用户名
int64 gender = 3; // 用户性别
}
// 定义 UserService 服务
service UserService {
// 获取用户信息
rpc GetUserInfo(GetUserInfoRequest) returns (GetUserInfoResponse);
}
MySQL
goctl指令
goctl model mysql ddl -src="./user.sql" -dir="./sql/model"
goctl model mysql ddl -src="./order.sql" --dir="./sql/model"
User
CREATE TABLE user (
id bigint AUTO_INCREMENT,
name varchar(255) NULL COMMENT '用户名',
password varchar(255) NOT NULL DEFAULT '' COMMENT '密码',
mobile varchar(255) NOT NULL DEFAULT '' COMMENT '手机号码',
gender char(10) NOT NULL DEFAULT 'male' COMMENT '性别,male|female|unknown',
nickname varchar(255) NULL DEFAULT '' COMMENT '昵称',
type tinyint(1) NULL DEFAULT 0 COMMENT '用户类型, 0:普通用户,1:VIP用户,用于测试golang关键字',
create_at timestamp NULL,
update_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE mobile_index (mobile),
UNIQUE name_index (name),
PRIMARY KEY (id)
) ENGINE = InnoDB COLLATE utf8mb4_general_ci COMMENT '用户表';
Order
CREATE TABLE `order` (
`order_sn` VARCHAR(64) NOT NULL COMMENT '订单号',
`user_id` BIGINT NOT NULL DEFAULT 0 COMMENT '用户ID',
`goods_id` BIGINT NOT NULL DEFAULT 0 COMMENT '商品ID',
`num` BIGINT NOT NULL DEFAULT 0 COMMENT '数量',
`amount` BIGINT NOT NULL DEFAULT 0 COMMENT '金额',
`status` INT NOT NULL DEFAULT 0 COMMENT '状态',
`create_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`delete_at` DATETIME DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`order_sn`)
) ENGINE=InnoDB COLLATE utf8mb4_general_ci COMMENT='订单表';
MySQL配置和model操作
已經完成了專案結構的初步搭建,下一步我們進行資料庫的配置,這裡我們從頭開始,使用 docker 建立表。下面是相關的指令。
建立 MySQL 資料庫和表
# 使用 docker 拉取 docekr 的镜像
docker pull mysql:8.0
# 使用镜像创建容器
docker run -d --name testMySQl \ # 容器名称
-e MYSQL_ROOT_PASSWORD=123456 \ # 数据库密码
-e MYSQL_DATABASE=goframework \ # 数据库名称
-p 3307:3306 \ # 容器内 3306 端口映射到 3307,因为我的 3306 被占用了
-v $PWD/mysql-data:/var/lib/mysql \ # 将存储映射到外部
mysql:8.0 --default-authentication-plugin=mysql_native_password
# 进入容器
docker exec -it testMySQl /bin/sh
# 进入数据库
mysql -uroot -p123456
# 看一下已有的数据库
show databases;
# 创建一个数据库
create database user_order;
# 使用数据库
use user_order;;
# 看一下表
show tables;
# 创建一个用户表
CREATE TABLE `user` (
id bigint AUTO_INCREMENT,
name varchar(255) NULL COMMENT '用户名',
password varchar(255) NOT NULL DEFAULT '' COMMENT '密码',
mobile varchar(255) NOT NULL DEFAULT '' COMMENT '手机号码',
gender char(10) NOT NULL DEFAULT 'male' COMMENT '性别,male|female|unknown',
nickname varchar(255) NULL DEFAULT '' COMMENT '昵称',
type tinyint(1) NULL DEFAULT 0 COMMENT '用户类型, 0:普通用户,1:VIP用户,用于测试golang关键字',
create_at timestamp NULL,
update_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE mobile_index (mobile),
UNIQUE name_index (name),
PRIMARY KEY (id)
) ENGINE = InnoDB COLLATE utf8mb4_general_ci COMMENT '用户表';
# 创建订单表
CREATE TABLE `order` (
`order_sn` VARCHAR(64) NOT NULL COMMENT '订单号',
`user_id` BIGINT NOT NULL DEFAULT 0 COMMENT '用户ID',
`goods_id` BIGINT NOT NULL DEFAULT 0 COMMENT '商品ID',
`num` BIGINT NOT NULL DEFAULT 0 COMMENT '数量',
`amount` BIGINT NOT NULL DEFAULT 0 COMMENT '金额',
`status` INT NOT NULL DEFAULT 0 COMMENT '状态',
`create_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`delete_at` DATETIME DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`order_sn`)
) ENGINE=InnoDB COLLATE utf8mb4_general_ci COMMENT='订单表';

專案連線資料庫
- 先修改配置檔案,在配置中增加資料庫相關配置
root:123456@tcp(127.0.0.1:3307)/user_order?charset=utf8mb4&parseTime=True&loc=Local
├─┘└──┘└┘└────┘└──┘└─────┘└───────┘└───────┘└────┘
用户名 密码 协议 主机地址 端口 数据库名 字符集 时间解析 时区
-
然後修改 yaml檔案中的配置資訊,寫入具體配置
-
在 servicecontext 中增加資料庫相關配置,並連線資料庫表
-
在註冊邏輯中進行實現,增加資料寫入邏輯
之後進行測試,資料寫入


註冊
之前我們實現了簡單的資料庫操作,現在開始我們實現一個簡單的註冊功能
-
引數校驗(校驗引數合法性,判斷一下是不是空,還有兩次密碼認證—)
-
資料唯一性確認(檢查資料庫中是否存在已有資料)
-
加密加鹽(使用 md5 進行加密加鹽)
// 加密加盐
h := md5.New()
h.Write([]byte(req.Password))
h.Write(secret)
passwordStr := hex.EncodeToString(h.Sum(nil))
下面是效果驗證



快取
go-zero快取
首先講一下快取,簡單來說快取就是把下次可能用到的資料先存下來,這樣下次需要的時候就不用再慢慢去找,能有效提升載入速度和使用者體驗。
在我們日常生活中使用瀏覽器載入網頁一般都會進行快取,所以一般第一次載入都會很慢,我之前遇到過一個情況,部署在 GitHub Page 的網頁會一直快取著第一次載入的資料,很奇怪,要在瀏覽器進行單獨設定後才會解決這個問題
在電商場景或者是應用場景下,會將讀得特別多、計算成本高、不經常變化的資料或者是需要快速響應的資料進行快取,既是為了降低成本,也是為了提升效率。
快取最常用的資料庫就是 Redis,Redis 也在逐漸迭代,適配更多的快取場景。
在 go-zero中,可以透過”-c”引數設定快取的使用,我們在目前的使用者訂單系統進行簡單的快取實現,首先使用下面指令,生成快取相關程式碼
goctl model mysql ddl -src="./user.sql" --dir="./sql/cachemodel" -c
下面是有無快取程式碼的對比
// 有缓存
func (m *defaultUserModel) FindOneByMobile(ctx context.Context, mobile string) (*User, error) {
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
var resp User
err := m.QueryRowIndexCtx(ctx, &resp, userMobileKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) {
query := fmt.Sprintf("select %s from %s where `mobile` = ? limit 1", userRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, mobile); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
//无缓存
func (m *defaultUserModel) FindOneByMobile(ctx context.Context, mobile string) (*User, error) {
var resp User
query := fmt.Sprintf("select %s from %s where `mobile` = ? limit 1", userRows, m.table)
err := m.conn.QueryRowCtx(ctx, &resp, query, mobile)
switch err {
case nil:
return &resp, nil
case sqlx.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
有了快取需求之後,我們就要配置快取資料庫了
- 先看生成的帶快取的usermodel.go檔案並深入檢視資料結構的設計,我們能發現需要的新版的配置型別是cache.CacheConf,深入發現需要的引數在RedisConf中,這是我們需要在 yaml 檔案中設定的內容。
// NewUserModel returns a model for the database table.
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserModel {
return &customUserModel{
defaultUserModel: newUserModel(conn, c, opts...),
}
}
type (
// A ClusterConf is the config of a redis cluster that used as cache.
ClusterConf []NodeConf
// A NodeConf is the config of a redis node that used as cache.
NodeConf struct {
redis.RedisConf
Weight int `json:",default=100"`
}
)
type (
// A RedisConf is a redis config.
RedisConf struct {
Host string
Type string `json:",default=node,options=node|cluster"`
User string `json:",optional"`
Pass string `json:",optional"`
Tls bool `json:",optional"`
NonBlock bool `json:",default=true"`
// PingTimeout is the timeout for ping redis.
PingTimeout time.Duration `json:",default=1s"`
}
// A RedisKeyConf is a redis config with key.
RedisKeyConf struct {
RedisConf
Key string
}
)
-
然後修改 config.go和 yaml 檔案我們簡單配置
-
然後就結束了,剩下的框架都給你實現了。
docker使用 redis
# 拉取镜像
docker pull redis:8
# 创建容器
docker run -d --name testRedis -p 6379:6379 redis:8
# 进入容器
docker exec -it testRedis redis-cli
# 看一下keys
keys cache:user:name:*
# 退出
exit
測試
透過結合 postman測試發現會逐漸將搜尋加入快取

這只是一個簡單的使用,實際 redis的功能和應用範圍非常廣泛
日誌
在 config預設的結構體中嵌入了 go-zero的log配置項,下面是他的完整設計,我們在 yaml檔案中建立 Log並進行下面引數的配置
# 配置
Log:
ServiceName: user-api-srv
Mode: file
Encoding: json
Path: logs
Level: info
Stat: true
type (
// A LogConf is a logging config.
LogConf struct {
// ServiceName represents the service name.
ServiceName string `json:",optional"`
// Mode represents the logging mode, default is `console`.
// console: log to console.
// file: log to file.
// volume: used in k8s, prepend the hostname to the log file name.
Mode string `json:",default=console,options=[console,file,volume]"`
// Encoding represents the encoding type, default is `json`.
// json: json encoding.
// plain: plain text encoding, typically used in development.
Encoding string `json:",default=json,options=[json,plain]"`
// TimeFormat represents the time format, default is `2006-01-02T15:04:05.000Z07:00`.
TimeFormat string `json:",optional"`
// Path represents the log file path, default is `logs`.
Path string `json:",default=logs"`
// Level represents the log level, default is `info`.
Level string `json:",default=info,options=[debug,info,error,severe]"`
// MaxContentLength represents the max content bytes, default is no limit.
MaxContentLength uint32 `json:",optional"`
// Compress represents whether to compress the log file, default is `false`.
Compress bool `json:",optional"`
// Stat represents whether to log statistics, default is `true`.
Stat bool `json:",default=true"`
// KeepDays represents how many days the log files will be kept. Default to keep all files.
// Only take effect when Mode is `file` or `volume`, both work when Rotation is `daily` or `size`.
KeepDays int `json:",optional"`
// StackCooldownMillis represents the cooldown time for stack logging, default is 100ms.
StackCooldownMillis int `json:",default=100"`
// MaxBackups represents how many backup log files will be kept. 0 means all files will be kept forever.
// Only take effect when RotationRuleType is `size`.
// Even though `MaxBackups` sets 0, log files will still be removed
// if the `KeepDays` limitation is reached.
MaxBackups int `json:",default=0"`
// MaxSize represents how much space the writing log file takes up. 0 means no limit. The unit is `MB`.
// Only take effect when RotationRuleType is `size`
MaxSize int `json:",default=0"`
// Rotation represents the type of log rotation rule. Default is `daily`.
// daily: daily rotation.
// size: size limited rotation.
Rotation string `json:",default=daily,options=[daily,size]"`
// FileTimeFormat represents the time format for file name, default is `2006-01-02T15:04:05.000Z07:00`.
FileTimeFormat string `json:",optional"`
// FieldKeys represents the field keys.
FieldKeys fieldKeyConf `json:",optional"`
}
fieldKeyConf struct {
// CallerKey represents the caller key.
CallerKey string `json:",default=caller"`
// ContentKey represents the content key.
ContentKey string `json:",default=content"`
// DurationKey represents the duration key.
DurationKey string `json:",default=duration"`
// LevelKey represents the level key.
LevelKey string `json:",default=level"`
// SpanKey represents the span key.
SpanKey string `json:",default=span"`
// TimestampKey represents the timestamp key.
TimestampKey string `json:",default=@timestamp"`
// TraceKey represents the trace key.
TraceKey string `json:",default=trace"`
// TruncatedKey represents the truncated key.
TruncatedKey string `json:",default=truncated"`
}
)
可以使用下面的介面,進行日誌的記錄
type Logger interface {
Debug(...any)
Debugf(string, ...any)
Debugv(any)
Debugw(string, ...LogField)
Error(...any)
Errorf(string, ...any)
Errorv(any)
Errorw(string, ...LogField)
Info(...any)
Infof(string, ...any)
Infov(any)
Infow(string, ...LogField)
Slow(...any)
Slowf(string, ...any)
Slowv(any)
Sloww(string, ...LogField)
WithCallerSkip(skip int) Logger
WithContext(ctx context.Context) Logger
WithDuration(d time.Duration) Logger
WithFields(fields ...LogField) Logger
}
簡單測試

login 和userInfo介面
主要進行這兩個檔案中邏輯的完善

在專案中進行簡單實現,具體參照專案程式碼,點選直達
下面是測試:



不過專案中還有很多錯誤,比如 id的寫入出現問題,涉及到資料庫配置的問題。
JWT 鑑權
JWT的主要流程
使用者詳情介面需要登陸之後才能進行訪問
-
使用者登入成功之後->JSON Web Token(JWT)->前端會把token儲存起來,每次後端呼叫的時候都會帶上,繼續驗證。
-
後端需要鑑權介面,通常是做成中介軟體的形式進行鑑權,對請求頭中的token進行解析
-
解析成功就是登入使用者
-
解析失敗就是未登入使用者或者是失效使用者
-
Refresh token
簡單實現和測試
藉助github.com/golang-jwt/jwt/v4實現 jwt 認證
還有一個比較重要的問題,jwt token存到哪裡了,存到專案的 context 中了,我們從 context 中就能取到值。
token := l.ctx.Value("userId")
生成 token

測試沒加 token 和加入 token 之後的人一個結果,注意會超時,因為之前設定了 60 秒。


中介軟體
路由中介軟體:只給部分路由新增中介軟體,比如鑑權,效能檢測等等
全域性中介軟體:全部都要使用
也能透過閉包呼叫其他服務中的中介軟體
中介軟體定義
我們可以直接在 api檔案中進行定義,透過 middleware 中介軟體
syntax = "v1"
// 进行项目的简单说明
info (
title: "实现简单的用户服务"
author: "aorangehc"
date: "2025年 11 月 11 日"
version: "v1"
)
// 注册需要用户名、密码、手机号码,要做唯一性检测
type RegisterReq {
UserName string `json:"username"`
Password string `json:"password"`
Repassword string `json:"re_password"`
Mobile string `json:"mobile"`
Gender int `json:"gender,options=0|1|2,default=0"` // male|female|unknown
NickName string `json:"nickname"`
}
type RegisterResp {
success bool `json:"success"` // 注册是否成功
Message string `json:"message"` // 注册报错信息
Id int64 `json:"id"` // 成功注册返回的 id
}
type LoginReq {
UserName string `json:"username"`
Password string `json:"password"`
}
type LoginResp {
Message string `json:"message"`
AccessToken string `json:"accessToken"`
AccessExpire int64 `json:"accessExpire"`
RefreshAfter int64 `json:"refreshAfter"`
}
type UserInfoReq {
Id int64 `json:"id"`
}
type UserInfoResp {
Id int64 `json:"id"`
Token string `json:"token"`
UserName string `json:"username"`
Mobile string `json:"mobile"`
Gender int `json:"gender,options=0|1|2,default=0"` // male|female|unknown
NickName string `json:"nickname"`
CreateAt string `json:"createAt"` // 注册时间
UpdateAt string `json:"updateAt"` // 最后更新时间
}
// 用户信息更新
type UserUpdateReq {
Id int64 `json:"id"`
UserName string `json:"username"`
Password string `json:"password"`
Mobile string `json:"mobile"`
Gender int `json:"gender,options=0|1|2,default=0"` // male|female|unknown
NickName string `json:"nickname"`
}
type UserUpdateResp {
success bool `json:"success"`
Message string `json:"message"` // 更新报错信息
}
// 定义用户服务的 Api
@server (
prefix: api
)
service user-api {
@handler register
post /user/register (RegisterReq) returns (RegisterResp)
@handler login
post /user/login (LoginReq) returns (LoginResp)
}
@server (
prefix: api
jwt: Auth
middleware: Cost // 耗时统计的一个中间件
)
service user-api {
@handler userInfo // 获取用户信息
get /user/:id (UserInfoReq) returns (UserInfoResp)
@handler userUpdate // 更新用户信息
post /user/update (UserUpdateReq) returns (UserUpdateResp)
}
Demo 和測試
更新完成 api檔案之後,透過 goctl更新專案,然後完善
GoZeroDemo/serviceDemo/user/api/internal/middleware/costMiddleware.go中的 Handle 方法
GoZeroDemo/serviceDemo/user/api/internal/svc/serviceContext.go中增加 Cost的注册
之後我們執行專案進行測試,列印出耗時


實現一個簡單的全域性中介軟體

RPC服務和呼叫
RPC 服務實現流程
-
編寫 rpc檔案
-
goctl生成專案檔案
-
修改配置結構體和相關的檔案
-
完善專案內容
-
修改 context 檔案
-
完善 RPC 業務邏輯
-
-
測試,使用 postman 測試功能

RPC 呼叫
在 go-zero中進行rpc呼叫基本要進行以下流程
-
首先配置 yaml 檔案和config 檔案
-
然後在 context 檔案中進行資料庫連線和 RPC 的配置
-
之後再 logic 檔案中進行 RPC 的呼叫
下面是測試結果
建立訂單

測試訂單刪除功能


獲取訂單資訊

註冊中心-consul
原本使用 Etcd 作為 註冊中心,要改成 consul,docker 啟動 consul
-
修改配置檔案 yaml 和 config.go,註釋掉原來的 etcd
-
將服務註冊到 consul,在啟動服務的時候
-
服務發現方也需要修改配置
之後啟動服務進行測試,測試成功



RPC 呼叫傳遞 metadata
metadata(後設資料)= 快遞單(貼在包裹上的資訊:誰寄的、寄給誰、是否易碎)。可以包含:
-
認證令牌 (Token): JWT Token,用來證明你是誰。
-
鏈路追蹤 ID (Trace ID): 用來追蹤這個請求經過了哪幾個微服務(例如 x-request-id)。
-
客戶端資訊: 比如 User-Agent,或者客戶端版本號。
-
超時控制: 告訴服務端,如果 1 秒沒處理完就別處理了。
RPC 攔截器
攔截器 = 安檢員/分揀中心(在包裹真正到達收件人手裡之前,先檢查一下快遞單,或者對包裹做個消毒)
裝“上下文”資訊
它是一種鉤子機制(Hook)。它允許你在請求傳送出去之前(客戶端)或者請求被處理之前/之後(服務端),插入一段通用的邏輯程式碼。
它的核心目的是把通用邏輯從業務程式碼中剝離出來(AOP 面向切面程式設計的思想)。可以進行:
-
身份認證: 在進入業務邏輯前,檢查 Metadata 裡有沒有 Token,Token 對不對。如果不對應,直接攔截退回,業務邏輯根本不用寫判斷程式碼。
-
日誌記錄: 記錄“誰在什麼時間調了哪個介面,耗時多少”。
-
錯誤處理: 統一捕獲異常,轉換成標準錯誤碼。
-
限流/熔斷: 檢查請求是不是太多了,如果太多直接攔截。
客戶端攔截器
在 context 檔案中編寫攔截器,之後加到 rpc發現服務中
需要注意:
-
什麼時候儲存metadata
-
怎麼存
-
攔截器怎麼透過 context 傳值
服務端攔截器
在啟動 RPC 服務之前進行攔截器的註冊
攔截器的業務邏輯實現
服務端怎麼從 metadata 中取到資料
測試
經過簡單測試,服務端收到來客戶端的後設資料並進行安正

錯誤處理
可以自定義錯誤型別,輸出更加全面
-
定義自定義格式錯誤
-
業務程式碼中按需使用定義的錯誤
-
告訴 go-zero框架我們自定義了錯誤型別要進行處理
下面是測試結果

修改模板生成自定義程式碼
模版的作用:生成專案程式碼,goctl指令生成程式碼的時候就是根據模板來生成程式碼的
修改模版的主要指令
goctl template clean # 用于删除持久化在本地的模板文件。
goctl template init # 用于初始化模板,会将模板文件存储到本地。
goctl template revert # 用于回滚某个分类下的指定的模板文件。
goctl template update # 用于更新某个分类下的所有模板文件。
這樣在指定目錄下就能看到.goctl資料夾,裡面儲存 tpl檔案,我們可以修改檔案內容,實現生成專案檔案的自動定義