目标
将 gRPC 的 .proto、.pb.go、_grpc.pb.go 统一保存在 底层仓库的 api/ 目录 下,同时让上层 服务直接依赖该 api 模块。
目录结构
go
仓库名/
├── api/
│ ├── go.mod
│ ├── category/v1/
│ │ ├── category.proto
│ │ ├── category.pb.go
│ │ └── category_grpc.pb.go
│ ├── invoice/v1/
│ │ ├── invoice.proto
│ │ ├── invoice.pb.go
│ │ └── invoice_grpc.pb.go
│ └── operation/v1/
│ ├── homepage_recommendation.proto
│ ├── homepage_recommendation.pb.go
│ └── homepage_recommendation_grpc.pb.go
├── cmd/
├── configs/
├── internal/
└── third_party/
操作
1.在 api/ 目录下新增独立 go.mod 文件
文件:api/go.mod
go
module ****.git(仓库git地址)/api
go 1.25.0
require (
google.golang.org/grpc v1.80.0
google.golang.org/protobuf v1.36.11
)
require (
golang.org/x/net v0.52.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
)
注意:
●module 写 仓库路径 + /api
●不要写网页地址
●不要写 /tree/xxx
●不要把分支名写进 module path
2.为每个 proto 设置正确的 go_package
例子
api/category/v1/category.proto
go
option go_package = "****.git(仓库git地址)/api/category/v1;v1";
proto全部内容
go
syntax = "proto3";
package agent.v1;
option go_package = "****.git(仓库git地址)/api/agent/v1;v1";
import "google/protobuf/timestamp.proto";
service AgentService {
rpc GetAgentInfoList(GetAgentInfoListReq) returns (GetAgentInfoListResp);
}
message GetAgentInfoListReq {
repeated int32 idList = 1;
string platformType = 2; // 客户端类型[pc/android/ios/harmony/h5]
}
message GetAgentInfoListResp {
repeated AgentInfo agentInfoList = 1;
}
message AgentInfo {
int64 id = 1; // 主键id
string name = 2; // 名称
string description = 3; // 描述
string iconUrl = 4; // 图标
string category = 5; // 分类
int32 status = 6; // 状态[0=删除 1=未删除]
int32 convSource = 7; // 会话源
string tag = 8; // 标签数组[逗号分隔]
string creator_id = 9; // 创建人id
string lastUpdaterId = 10; // 最后更新人id
google.protobuf.Timestamp createTime = 11; // 创建时间
google.protobuf.Timestamp lastUpdateTime = 12;// 更新时间
string statusMsg = 13; // 状态消息
int32 hot = 14; // 热门
int32 subStatus = 15; // 订阅状态 1=已订阅 2=未订阅
int32 sortNum = 16; // 排序字段,越小越靠前
}
3.生成代码后提交到仓库
生成后的文件包括:
●*.pb.go
●*_grpc.pb.go
这些文件与 .proto 一起保存在 api/ 目录下,并正常提交 Git。
4 上层服务直接依赖 api 模块
在 上层项目中执行:
go
go get ***.git(仓库git地址)/api@<tag或commit>
go
import categoryv1 " ***.git(仓库git地址)/api/category/v1"
import invoicev1 " ***.git(仓库git地址)/api/invoice/v1"
如果嫌导入方式很长, 可以用 go.mod 的 replace 进行简化, 目前 esp-console 项目将其简写为 esp-grpc
●go.mod 文件:
go
// module aira-aiplatform-esp-console
module aira-aiplatform-esp-console
go 1.25.0
require (
esp-grpc v0.0.0
// ...
)
// go get ***.git(仓库git地址)/api@commit-or-tag
replace esp-grpc => ***.git(仓库git地址)/api v0.0.0-20260409054836-7902fa4f81a0
require (
// indirect requires...
)
●go文件的 import:
go
import (
invoicev1 "esp-grpc/invoice/v1"
operationv1 "esp-grpc/operation/v1"
// ...
)
私有仓库配置
因为是私有仓库,需要在本地或 CI 配置:
go
go env -w GOPRIVATE=codeup.aliyun.com(仓库域名)
作用:私有库不走代理、不校验 sum
并确保当前机器已经具备 Codeup 的 Git 拉取权限。
执行 go mod tidy
也可使用下面的shell脚本直接执行拉取底层被依赖项目的代码
powershell
#!/usr/bin/env bash
set -euo pipefail
readonly TARGET_DEPENDENCY="esp-grpc"## 上层项目import 底层项目的简称
readonly REPLACEMENT_MODULE=" ***.git(仓库git地址)/api" ##被依赖的grpc仓库地址
readonly VERSION_TEMPLATE='{{.Version}}'
usage() {
cat <<'EOF'
Usage:
scripts/update_esp_grpc_replace.sh <commit-hash>
Example:
scripts/update_esp_grpc_replace.sh e4251adf9639
EOF
}
main() {
if [[ $# -ne 1 ]]; then
usage
exit 1
fi
local commit_hash="$1"
local resolved_version
resolved_version="$(go list -m -f "${VERSION_TEMPLATE}" "${REPLACEMENT_MODULE}@${commit_hash}")"
if [[ -z "${resolved_version}" ]]; then
echo "Failed to resolve version for commit: ${commit_hash}" >&2
exit 1
fi
go mod edit -replace="${TARGET_DEPENDENCY}=${REPLACEMENT_MODULE}@${resolved_version}"
go mod tidy
echo "Updated ${TARGET_DEPENDENCY} replace version to ${resolved_version}"
}
main "$@"
在上层代码执行以上shell脚本,./shell脚本 commit哈希值