diff --git a/README.md b/README.md index b90beeb..e522714 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,6 @@ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest ``` - - ### 1. 创建 RPC 服务(goctl) 在仓库根目录执行: @@ -55,8 +53,6 @@ goctl rpc new order cd order ``` - - ### 2. 将 proto 移到 `proto/` 目录 `goctl rpc new` 默认把 proto 放在服务根目录(如 `order/order.proto`)。统一挪到 `proto/`: @@ -66,8 +62,6 @@ mkdir proto mv order.proto proto/ ``` - - ### 3. Zero 兼容 Nacos 等配置 编辑 `order/internal/config/config.go`: @@ -88,8 +82,6 @@ type NacosConf struct { } ``` - - ### 4. 配置服务名与监听端口 编辑 `order/etc/order.yaml`: @@ -104,8 +96,6 @@ Nacos: ConfigID: develop-admin ``` - - ### 5. 构建 mod 编辑 `order/go.mod` 增加本地包 pkg.local @@ -126,11 +116,11 @@ replace pkg.local => ../pkg ```go "pkg.local/discovery" -"pkg.local/log" "pkg.local/modelbase" "pkg.local/mysql" "pkg.local/redis" "pkg.local/validate" +"pkg.local/utils" ``` 增加初始化 @@ -138,135 +128,127 @@ replace pkg.local => ../pkg ```go func main() { -flag.Parse() -var c config.Config -conf.MustLoad(*configFile, &c) + flag.Parse() + var c config.Config + conf.MustLoad(*configFile, &c) -// Nacos配置拉取初始化 -nacosParam := utils.NacosConfig{ -Hosts: c.Nacos.Hosts, -NamespaceId: c.Nacos.NamespaceId, -Group: c.Nacos.Group, -ConfigID: c.Nacos.ConfigID, -} -utils.InitConfig(nacosParam) + nacosParam := utils.NacosConfig{ + Hosts: c.Nacos.Hosts, + NamespaceId: c.Nacos.NamespaceId, + Group: c.Nacos.Group, + ConfigID: c.Nacos.ConfigID, + } + utils.InitConfig(nacosParam) -// 日志配置 -logConf := logx.LogConf{ -ServiceName: utils.GetConfigString("log.serviceName"), -Mode: utils.GetConfigString("log.mode"), -Encoding: utils.GetConfigString("log.encoding"), -Level: utils.GetConfigString("log.level"), -Path: utils.GetConfigString("log.path"), -KeepDays: utils.GetConfigInt("log.keepDays"), -MaxSize: utils.GetConfigInt("log.maxSize"), -MaxBackups: utils.GetConfigInt("log.maxBackups"), -Compress: utils.GetConfigBool("log.compress"), -} -logx.SetUp(logConf) -logx.AddWriter(logx.NewWriter(os.Stdout)) + logConf := logx.LogConf{ + ServiceName: utils.GetConfigString("log.serviceName"), + Mode: utils.GetConfigString("log.mode"), + Encoding: utils.GetConfigString("log.encoding"), + Level: utils.GetConfigString("log.level"), + Path: utils.GetConfigString("log.path"), + KeepDays: utils.GetConfigInt("log.keepDays"), + MaxSize: utils.GetConfigInt("log.maxSize"), + MaxBackups: utils.GetConfigInt("log.maxBackups"), + Compress: utils.GetConfigBool("log.compress"), + } + logx.SetUp(logConf) + logx.AddWriter(logx.NewWriter(os.Stdout)) -listenOn := utils.GetConfigString("base.listenOn") -mode := utils.GetConfigString("base.mode") -serviceName := utils.GetConfigString("base.name") -registerIP := utils.GetConfigString("base.registerIP") + listenOn := utils.GetConfigString("base.listenOn") + mode := utils.GetConfigString("base.mode") + serviceName := utils.GetConfigString("base.name") + registerIP := utils.GetConfigString("base.registerIP") -// 初始化服务发现 -if err := discovery.Init(discovery.Config{ -Hosts: c.Nacos.Hosts, -NamespaceId: c.Nacos.NamespaceId, -Group: c.Nacos.Group, -}); err != nil { -logx.Errorf("nacos init: %v", err) -os.Exit(1) -} + if err := discovery.Init(discovery.Config{ + Hosts: c.Nacos.Hosts, + NamespaceId: c.Nacos.NamespaceId, + Group: c.Nacos.Group, + }); err != nil { + logx.Errorf("nacos init: %v", err) + os.Exit(1) + } -port, err := listenPort(listenOn) -if err != nil { -logx.Errorf("parse ListenOn: %v", err) -os.Exit(1) -} + port, err := listenPort(listenOn) + if err != nil { + logx.Errorf("parse ListenOn: %v", err) + os.Exit(1) + } -// 注册服务 -if err := discovery.Register(discovery.Instance{ -ServiceName: serviceName, -IP: registerIP, -Port: port, -Group: c.Nacos.Group, -}); err != nil { -logx.Errorf("nacos register: %v", err) -os.Exit(1) -} + if err := discovery.Register(discovery.Instance{ + ServiceName: serviceName, + IP: registerIP, + Port: port, + Group: c.Nacos.Group, + }); err != nil { + logx.Errorf("nacos register: %v", err) + os.Exit(1) + } -defer func() { -if err := discovery.Deregister(); err != nil { -logx.Errorf("nacos deregister: %v", err) -} -}() + defer func() { + if err := discovery.Deregister(); err != nil { + logx.Errorf("nacos deregister: %v", err) + } + }() -// 初始化MySQL -db, err := mysql.New(mysql.Config{ -Host: utils.GetConfigString("mysql.host"), -Port: utils.GetConfigInt("mysql.port"), -User: utils.GetConfigString("mysql.user"), -Password: utils.GetConfigString("mysql.password"), -Database: utils.GetConfigString("mysql.database"), -Charset: utils.GetConfigString("mysql.charset"), -Prefix: utils.GetConfigString("mysql.prefix"), -ReadHost: utils.GetConfigString("mysql_read.host"), -ReadPort: utils.GetConfigInt("mysql_read.port"), -ReadUser: utils.GetConfigString("mysql_read.user"), -ReadPassword: utils.GetConfigString("mysql_read.password"), -ReadDatabase: utils.GetConfigString("mysql_read.database"), -}) -if err != nil { -logx.Errorf("mysql init: %v", err) -os.Exit(1) -} + db, err := mysql.New(mysql.Config{ + Host: utils.GetConfigString("mysql.host"), + Port: utils.GetConfigInt("mysql.port"), + User: utils.GetConfigString("mysql.user"), + Password: utils.GetConfigString("mysql.password"), + Database: utils.GetConfigString("mysql.database"), + Charset: utils.GetConfigString("mysql.charset"), + Prefix: utils.GetConfigString("mysql.prefix"), + ReadHost: utils.GetConfigString("mysql_read.host"), + ReadPort: utils.GetConfigInt("mysql_read.port"), + ReadUser: utils.GetConfigString("mysql_read.user"), + ReadPassword: utils.GetConfigString("mysql_read.password"), + ReadDatabase: utils.GetConfigString("mysql_read.database"), + }) + if err != nil { + logx.Errorf("mysql init: %v", err) + os.Exit(1) + } -// 初始化Redis -if err := redis.Init(redis.Config{ -Host: utils.GetConfigString("redis.host"), -Port: utils.GetConfigInt("redis.port"), -Password: utils.GetConfigString("redis.password"), -DB: utils.GetConfigInt("redis.db"), -}); err != nil { -logx.Errorf("redis init: %v", err) -os.Exit(1) -} + if err := redis.Init(redis.Config{ + Host: utils.GetConfigString("redis.host"), + Port: utils.GetConfigInt("redis.port"), + Password: utils.GetConfigString("redis.password"), + DB: utils.GetConfigInt("redis.db"), + }); err != nil { + logx.Errorf("redis init: %v", err) + os.Exit(1) + } -debug := utils.GetConfigBool("mysql.debug") -modelbase.Init(db, modelbase.Config{Prefix: utils.GetConfigString("mysql.prefix"), Debug: debug}) + debug := utils.GetConfigBool("mysql.debug") + modelbase.Init(db, modelbase.Config{Prefix: utils.GetConfigString("mysql.prefix"), Debug: debug}) -// 修正语法错误,手动组装RPC配置 -rpcConf := zrpc.RpcServerConf{ -ListenOn: listenOn, -} -rpcConf.Mode = mode -// 构建上下文 -ctx := svc.NewServiceContext(c, db) + rpcConf := zrpc.RpcServerConf{ + ListenOn: listenOn, + } + rpcConf.Mode = mode -// 启动rpc服务,使用拼装好的rpcConf,不再使用本地空的c.RpcServerConf -s := zrpc.MustNewServer(rpcConf, func(grpcServer *grpc.Server) { -admin.RegisterAdminServer(grpcServer, server.NewAdminServer(ctx)) -if mode == service.DevMode || mode == service.TestMode { -reflection.Register(grpcServer) -} -}) -defer s.Stop() + ctx := svc.NewServiceContext(c, db) -s.AddUnaryInterceptors(validate.UnaryServerInterceptor(validate.MustNew())) + s := zrpc.MustNewServer(rpcConf, func(grpcServer *grpc.Server) { + order.RegisterOrderServer(grpcServer, server.NewOrderServer(ctx)) + if mode == service.DevMode || mode == service.TestMode { + reflection.Register(grpcServer) + } + }) + defer s.Stop() -logx.Infof("Starting rpc server at %s...", listenOn) -s.Start() + s.AddUnaryInterceptors(validate.UnaryServerInterceptor(validate.MustNew())) + + logx.Infof("Starting rpc server at %s...", listenOn) + s.Start() } func listenPort(listenOn string) (uint64, error) { -_, portStr, err := net.SplitHostPort(listenOn) -if err != nil { -return 0, err -} -return strconv.ParseUint(portStr, 10, 64) + _, portStr, err := net.SplitHostPort(listenOn) + if err != nil { + return 0, err + } + return strconv.ParseUint(portStr, 10, 64) } ``` @@ -297,8 +279,6 @@ func NewServiceContext(c config.Config, db *gorm.DB) *ServiceContext { ``` - - ### 7. 接入 Docker Compose 在 `deploy/docker-compose.dev.override.yml` 增加服务: @@ -320,30 +300,14 @@ func NewServiceContext(c config.Config, db *gorm.DB) *ServiceContext { - rnacos ``` - - ### 8. 生成 ProtoSet 给 BFF ```bash - protoc --include_imports --proto_path=proto --descriptor_set_out=../bff/etc/order.pb order.proto ``` - - ### 9. 在 BFF 配 Upstream(HTTP 路由写在 proto) -在对应 RPC 上声明 `google.api.http`(需 `import "google/api/annotations.proto";`): - -```protobuf -rpc Ping(Request) returns (Response) { - option (google.api.http) = { - post: "/admin/v3/order/ping" - body: "*" - }; -} -``` - 编辑 `bff/etc/bff.yaml`,在 **Upstreams** 增加一段(不必写 Mappings,gateway 会从 ProtoSet 里的 http option 注册路由): ```yaml @@ -355,7 +319,16 @@ rpc Ping(Request) returns (Response) { - etc/order.pb ``` +在对应 RPC 上声明 `google.api.http`(需 `import "google/api/annotations.proto";`): +```protobuf +rpc Ping(Request) returns (Response) { + option (google.api.http) = { + post: "/admin/v3/order/ping" + body: "*" + }; +} +``` ### 编写业务须知 @@ -366,8 +339,6 @@ rpc Ping(Request) returns (Response) { --- - - ### 命令 ①:生成服务端 pb / grpc ```bash @@ -379,8 +350,6 @@ goctl rpc protoc proto/order.proto --proto_path=. --proto_path=../pkg/third_part - 只改 **校验规则**(必填/长度/范围等)→ **只跑这条** - 增删改字段、增删 RPC → 也要跑(服务端描述符要更新) - - ### 命令 ②:生成 BFF ProtoSet ```bash @@ -393,8 +362,6 @@ protoc -I. -I../pkg/third_party --descriptor_set_out=../bff/etc/order.pb --inclu - 增删 **RPC** - 改 **google.api.http** 路由 - - ### 命令 ③:goctl 生成/更新脚手架 ```bash @@ -406,8 +373,6 @@ goctl -I. -I../pkg/third_party rpc protoc proto/order.proto --go_out=. --go-grpc - **新增 RPC**:需要生成 `internal/logic`、更新 `server` / `orderclient` 等 - 注意:可能覆盖已改过的 `order.go` 等,生成后对比合并;也可用手写 logic/server 代替 - - ## Proto 常用校验规则(protovalidate) 文档:[https://protovalidate.com/schemas/standard-rules/](https://protovalidate.com/schemas/standard-rules/) @@ -436,8 +401,6 @@ double price = 6 [(buf.validate.field).double = {gte: 0}]; int32 period_validity = 16 [(buf.validate.field).int32 = {gte: 0}]; ``` - - ### 非必填数值(0 表示未传,跳过校验) ```protobuf @@ -463,24 +426,18 @@ uint32 sales_model = 4 [(buf.validate.field) = { }]; ``` - - ### ID 必须大于 0 ```protobuf int64 id = 1 [(buf.validate.field).int64 = {gt: 0}]; ``` - - ### 无规则字段 ```protobuf uint32 number = 20; // 不做 protovalidate ``` - - ### 删除字段编号(避免复用) ```protobuf diff --git a/bff/etc/user.pb b/bff/etc/user.pb new file mode 100644 index 0000000..779c9dc --- /dev/null +++ b/bff/etc/user.pb @@ -0,0 +1,10 @@ + + + +user.protouser" +Request +ping ( Rping" +Response +pong ( Rpong2- +User% +Ping .user.Request.user.ResponseBZ./userbproto3 \ No newline at end of file