Files
2026-08-05 17:37:32 +08:00

6.9 KiB
Raw Permalink Blame History

如何加一个新服务

以加 order 为例(对标现有 product)。

0. 安装 goctl

go install github.com/zeromicro/go-zero/tools/goctl@latest
goctl --version
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

在仓库根目录执行:

goctl rpc new order
cd order

2. 将 proto 移到 proto/ 目录

goctl rpc new 默认把 proto 放在服务根目录(如 order/order.proto)。统一挪到 proto/

mkdir proto
mv order.proto proto/

4. 配置 Zero 兼容 Nacos

编辑 order/internal/config/config.go

package config

import "github.com/zeromicro/go-zero/zrpc"

type Config struct {
	zrpc.RpcServerConf
	Nacos NacosConf
}

type NacosConf struct {
	Hosts       []string
	NamespaceId string `json:",optional"`
	Group       string `json:",optional"`
	RegisterIP  string `json:",optional"`
}

5. 配置服务名与监听端口

order/etc/order.yaml

Name: order-service          # 注册到 Nacos 的 serviceNameBFF Upstream.Target 必须与此一致
ListenOn: 0.0.0.0:10200

Nacos:
  Hosts:
    - rnacos:8848
  NamespaceId: test
  Group: LONE_SERVICES
  RegisterIP: order          # Docker Compose 里的服务名 / 主机名

Mysql:
  Host: mysql
  Port: 3306
  User: root
  Password: "123123"
  Database: dms-order
  Charset: utf8mb4
  Prefix:

3. 构建 mod

编辑 order/go.mod

go 1.24.3

require (
	github.com/zeromicro/go-zero v1.10.2
	google.golang.org/grpc v1.80.0
	google.golang.org/protobuf v1.36.11
)
go mod tidy

编辑 order/go.mod 增加本地包 pkg.local

require (
	github.com/zeromicro/go-zero v1.10.2
	google.golang.org/grpc v1.80.0
	google.golang.org/protobuf v1.36.11
	pkg.local v0.0.0
)

replace pkg.local => ../pkg

6. 服务注册

编辑 order/order.go


func main() {
	flag.Parse()

	var c config.Config
	conf.MustLoad(*configFile, &c)
	if err := discovery.Init(discovery.Config{
		Hosts:       c.Nacos.Hosts,
		NamespaceId: c.Nacos.NamespaceId,
		Group:       c.Nacos.Group,
	}); err != nil {
		log.Fatalf("nacos init: %v", err)
	}

	registerIP := c.Nacos.RegisterIP
	port, err := listenPort(c.ListenOn)
	if err != nil {
		log.Fatalf("parse ListenOn: %v", err)
	}
	if err := discovery.Register(discovery.Instance{
		ServiceName: c.Name,
		IP:          registerIP,
		Port:        port,
		Group:       c.Nacos.Group,
	}); err != nil {
		log.Fatalf("nacos register: %v", err)
	}
	defer func() {
		if err := discovery.Deregister(); err != nil {
			log.Printf("nacos deregister: %v", err)
		}
	}()

	ctx := svc.NewServiceContext(c)

	s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
		order.RegisterOrderServer(grpcServer, server.NewOrderServer(ctx))

		if c.Mode == service.DevMode || c.Mode == service.TestMode {
			reflection.Register(grpcServer)
		}
	})
	defer s.Stop()

	fmt.Printf("Starting rpc server at %s...\n", c.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)
}

编辑 order/order.go:增加本地包

import (
	"pkg.local/discovery"
)

再次执行

go mod tidy

8. 接入 Docker Compose

deploy/docker-compose.override.yml 增加服务:

  order:
    image: golang:1.24.3
    volumes:
      - ..:/src
      - go-mod-cache:/go/pkg/mod
    working_dir: /src/order
    command: go run . -f etc/order.yaml
    ports:
      - "10200:10200"
    environment:
      NACOS_SERVER_ADDR: rnacos:8848
    depends_on:
      - rnacos

9. 生成 ProtoSet 给 BFF


protoc --include_imports --proto_path=proto --descriptor_set_out=../bff/etc/order.pb order.proto

10. 在 BFF 配 Upstream + Mapping

编辑 bff/etc/bff.yaml,增加一段(go-zero gateway 每个 RPC 都要写 Mapping):

  - Name: order
    Grpc:
      Target: order-service   # 必须等于 order.yaml 的 Name
      Timeout: 5000
    ProtoSets:
      - etc/order.pb
    Mappings:
      - Method: post
        Path: /admin/v3/order/ping
        RpcPath: order.Order/Ping

11. 加接口需要

只改业务逻辑(internal/logic/)不必跑 goctl。 改 proto/order.proto(新增/改名 RPC、改字段等)后,按文末「三条命令何时用」处理;

若 HTTP 路径有变,再改第 10 步的 Mapping。


命令 ①:生成服务端 pb / grpc

protoc -I. -I../pkg/third_party --go_out=. --go-grpc_out=. proto/order.proto

什么时候跑:

  • 只改 校验规则(必填/长度/范围等)→ 只跑这条
  • 增删改字段、增删 RPC → 也要跑(服务端描述符要更新)

命令 ②:生成 BFF ProtoSet

protoc -I. -I../pkg/third_party --descriptor_set_out=../bff/etc/product.pb --include_imports proto/order.proto

什么时候跑:

  • 增删改 字段BFF gateway 编解码需要)
  • 增删 RPC

命令 ③:goctl 生成/更新脚手架

goctl rpc protoc proto/order.proto --go_out=. --go-grpc_out=. --zrpc_out=.

什么时候跑:

  • 新增 RPC:需要生成 internal/logic、更新 server / productclient
  • 注意:可能覆盖已改过的 product.go 等,生成后对比合并;也可用手写 logic/server 代替

Proto 常用校验规则(protovalidate

文档:https://protovalidate.com/schemas/standard-rules/ 依赖:import "buf/validate/validate.proto";,服务启动挂 pkg.local/validate 拦截器。

必填字符串

string name = 1 [(buf.validate.field).string = {min_len: 1, max_len: 256}];

min_len: 1 表示不能为空串。

可选字符串(仅限长度)

string subhead = 2 [(buf.validate.field).string = {max_len: 255}];

空串可通过;有内容时限制最大长度。

必填数值(含 0 合法)

double price = 6 [(buf.validate.field).double = {gte: 0}];
int32 period_validity = 16 [(buf.validate.field).int32 = {gte: 0}];

非必填数值(0 表示未传,跳过校验)

double store_price = 7 [(buf.validate.field) = {
  ignore: IGNORE_IF_ZERO_VALUE,
  double: {gte: 0}
}];

有值时仍要求 >= 0;未传/为 0 不校验。

枚举 / 固定取值(0 表示未传)

uint32 type = 19 [(buf.validate.field) = {
  ignore: IGNORE_IF_ZERO_VALUE,
  uint32: {in: [1, 2, 3]}
}];

uint32 sales_model = 4 [(buf.validate.field) = {
  ignore: IGNORE_IF_ZERO_VALUE,
  uint32: {in: [1, 2]}
}];

ID 必须大于 0

int64 id = 1 [(buf.validate.field).int64 = {gt: 0}];

无规则字段

uint32 number = 20;  // 不做 protovalidate

删除字段编号(避免复用)

// reserved 2;

业务条件校验(如「普通商品必须带齐价格」)仍写在 internal/logic,不要全塞进 proto。