go-zero 初探

标签:微服务首次发布:2023-12-09最近修改:2023-12-09

项目搭建

其实微服务框架的核心还是围绕 RPC 和 HTTP 通信构建的,提供了诸如服务发现、负载均衡、熔断、限流、跟踪、度量和配置管理等微服务必需的功能。比如利用.proto文件来定义服务接口和数据结构,然后使用相应的命令来自动生成 Go 的 gRPC 代码;通过.api文件和一些工具命令来生成 HTTP 服务的路由、请求和响应的处理代码。

go-zero 微服务实战系列(二、服务拆分)这篇文章详细的介绍了如何搭建一个微服务项目。下面是我对这篇文章的笔记补充和总结:

微服务拆分的补充

商城系统作为一个复杂的业务系统,包括商品管理、订单处理、用户管理、支付处理、库存管理、推荐和营销等多个方面。下面是根据业务职能和领域驱动设计(DDD)的界限上下文进行微服务划分的对比分析:

  1. 业务职能划分

    按照业务职能划分,我们会根据每个职能的操作和职责来定义服务的边界。在商城系统中,可以进行如下划分:

    • 商品服务:处理商品的上架、信息更新、分类、搜索等功能。

    • 订单服务:管理订单的创建、修改、查询和状态跟踪。

    • 用户服务:维护用户信息、权限、登录、注册等。

    • 支付服务:处理支付请求、支付状态、退款等。

    • 库存服务:管理库存数量、库存锁定、库存释放等。

    • 推荐服务:基于用户行为和购买历史推荐商品。

    • 营销服务:管理促销活动、优惠券等营销工具。

    每个服务都是围绕特定的业务职能构建的,职责清晰,开发和维护的团队可以专注于特定的业务区域。

  2. DDD 界限上下文划分

    DDD 提倡根据业务的边界上下文来划分服务,这通常是围绕业务流程和领域模型来组织代码和数据库模式的。在 DDD 中,界限上下文是具有明确界限的特定职责区域,在这之内部署了一致性的模型和接口。以商城系统为例,可能的界限上下文包括:

    • 产品目录上下文:包括商品的管理和展示,与其他上下文隔离,只关心商品的信息和分类。

    • 销售上下文:涉及订单处理的全部流程,从购物车管理到订单完成,可能还会包括支付和库存的交互。

    • 身份与访问管理上下文:处理用户的认证和鉴权,可能与订单服务交互以确定用户身份。

    • 库存管理上下文:独立处理库存,它将关注库存的状态和与订单服务的交互。

    • 支付上下文:处理所有支付流程,包括支付请求、验证和记录。

    • 推荐上下文:从用户行为、购买历史等多个上下文中收集数据,提供个性化推荐。

    在 DDD 中,服务的划分更加注重业务流程和领域逻辑的整体一致性,而不是单一的职能。它鼓励深入分析业务场景以发现领域模型,这些模型定义了实体、值对象、聚合根、领域事件等概念,并围绕这些模型构建服务。

  3. 对比

    • 专业知识要求:DDD 的划分方式要求团队对业务有更深入的理解,而业务职能划分可能更直观、易于理解。

    • 服务间的耦合:职能划分可能导致服务间的耦合度较高,因为它们通常是基于业务操作而不是业务流程来划分的。而 DDD 强调的是低耦合的上下文间关系。

    • 数据一致性:DDD 的划分可能更好地处理数据一致性问题,因为它通过聚合根和事务边界来控制数据的一致性。

    • 复杂性:按照 DDD 划分服务通常更复杂,需要更多的设计和模型发现工作,但最终可能提供更灵活和可扩展的系统架构。

    • 组织结构:DDD 的界限上下文划分使得团队可以更好地围绕业务能力组织,这与康威定律(Conway’s Law)相呼应,即组织架构会影响最终的系统设计。

项目结构的总结

关于项目的整体结构,大致如下:

text
├── Dockerfile 定义应用程序的容器化配置文件。├── (apps)/(internal) 存放内部代码的目录。├── cmd 包含各个微服务的入口点,例如main.go文件。├── conf 一些全局配置。├── pkg 用于存放公共库和工具代码└── go.mod

对于每一个微服务内部,apps 里面的目录结构大致如下:

text
├─app 对外的 BFF 服务,接受来自客户端的请求,暴露 HTTP 接口。├─product 商品微服务│  ├─admin 对内的服务,区别于 rpc,更多的是面向运营侧的且数据权限较高,通过隔离可带来更好的代码级别的安全,直接提供HTTP接口。│  │  ├─etc│  │  └─internal│  └─rpc 对内的微服务,仅接受来自内部其他微服务或者 BFF 的请求,暴露 gRPC 接口。│      ├─etc│      ├─internal│      ├─rpc│      └─rpcclient└─user 用户微服务    ├─admin    │  ├─etc    │  └─internal    └─rpc        ├─etc        ├─internal        ├─rpc        └─rpcclient

一些 goctl 命令总结

goctl api 命令

用法:

  • goctl api [flags]
  • goctl api [command]

flags 这里不做介绍,详细看一看 command 有哪些:

  • dart 为提供的 api 文件生成 dart 文件
  • doc 生成 doc 文件
  • format 格式化 api 文件
  • go 为提供的 api 文件生成 go 文件
  • kt 为提供的 api 文件生成 kotlin 代码
  • new 快速创建 api 服务
  • plugin 自定义文件生成器
  • ts 为提供的 api 文件生成 ts 文件
  • validate 验证 api 文件

1. goctl api doc 命令

根据 api 文件生成 markdown 文档。这个 Markdown 文件通常包含了 API 的详细文档,如 API 的路径、方法、请求参数、响应结构等信息,以及可能的注释。具体用法是:goctl api doc [flags]。其中 flags 可以是--dir--o。具体含义如下:

参数字段 参数类型 是否必填 默认值 参数说明
dir string YES 空字符串 后面的参数必须是.api文件所在目录的路径,这个目录的路径可以是相对路径或绝对路径
o string NO 当前的工作目录 指定 md 文件所在的目录的名字

2. goctl api format 命令

递归格式化指定目录下的 api 文件。具体用法:goctl api format [flags]。其中 flags 可以是--declare--dir--stdin

参数字段 参数类型 是否必填 默认值 参数说明
declare boolean NO false 是否检测上下文
dir string YES 空字符串 后面的参数必须是.api文件所在目录的路径,这个目录的路径可以是相对路径或绝对路径
stdin boolean NO false 是否格式化终端输入的 api 内容

3. goctl api go 命令

根据 api 文件生成 Go HTTP 代码。具体用法:goctl api go [flags]

参数字段 参数类型 是否必填 默认值 参数说明
api string YES 空字符串 api 文件路径
branch string NO 空字符串 远程模板所在 git 分支名称,仅当 remote 有值时使用
dir string NO 当前工作目录 代码输出目录
home string NO ${HOME}/.goctl 本地模板文件目录
remote string NO 空字符串 远程模板所在 git 仓库地址,当此字段传值时,优先级高于 home 字段值
style string NO gozero 输出文件和目录的命名风格格式化符号,详情见 文件风格

4. goctl api new 命令

快速生成 Go HTTP 服务,开发者需要在终端指定服务名称参数,输出目录为当前工作目录。具体用法:goctl api new [flags] server-name

参数字段 参数类型 是否必填 默认值 参数说明
branch string NO 空字符串 远程模板所在 git 分支名称,仅当 remote 有值时使用
home string NO ${HOME}/.goctl 本地模板文件目录
remote string NO 空字符串 远程模板所在 git 仓库地址,当此字段传值时,优先级高于 home 字段值
style string NO gozero 输出文件和目录的命名风格格式化符号,详情见 文件风格

5. goctl api plugin 命令

用于引用插件生成代码,开发者需要在终端指定插件名称、参数等信息。具体用法:goctl api plugin [flags]

参数字段 参数类型 是否必填 默认值 参数说明
api string YES 空字符串 api 文件路径
dir string NO 当前工作目录 指定了插件处理后的文件存放的位置
plugin string YES 空字符串 插件可执行文件所在路径,支持本地和 http 文件
style string NO gozero 输出文件和目录的命名风格格式化符号,详情见 文件风格

6. goctl api validate 命令

校验 api 文件是否符合规范。具体用法:goctl api validate [flags]

参数字段 参数类型 是否必填 默认值 参数说明
api string YES 空字符串 api 文件路径

goctl rpc 命令

用法:

  • goctl rpc [flags]
  • goctl rpc [command]

flags 这里不做介绍,详细看一看 command 有哪些:

  • new 生成 rpc 演示服务
  • protoc 生成 grpc 代码

1. goctl rpc new 命令

快速生成一个 rpc 服务,其接收一个终端参数来指定服务名称。具体用法:goctl rpc new [flags] rpc-name

参数字段 参数类型 是否必填 默认值 参数说明
branch string NO 空字符串 模板仓库分支,配合 --remote 使用
home string NO ~/.goctl 模板仓库本地路径,优先级高于 --remote
idea bool NO false 仅 idea 插件用,终端请忽略此字段
remote string NO 空字符串 模板仓库远程路径
style string NO gozero 文件命名风格,详情可参考 文件风格

2. goctl rpc protoc 命令

根据 protobufer 文件生成 rpc 服务。具体用法:goctl rpc protoc [flags]

参数字段 参数类型 是否必填 默认值 参数说明
branch string NO 空字符串 模板仓库分支,配合 --remote 使用
home string NO ~/.goctl 模板仓库本地路径,优先级高于 --remote
multiple bool NO false 是否生成多个 rpc 服务
remote string NO 空字符串 模板仓库远程路径
style string NO gozero 输出目录的文件命名风格,详情可参考 文件风格
zrpc_out string NO 空字符串 生成 go-zero 框架专用的 RPC 服务代码的目录

除了上述参数外,还支持 protoc 指令的原生参数。

示例:goctl rpc protoc demo.proto --go_out=./pb --go_grpc_out=./pb --zrpc_out=.

  1. –go_out 与 --go_grpc_out 生成的最终目录必须一致。
  2. –go_out & --go_grpc_out 和 --zrpc_out 的生成的最终目录必须不为同一目录。
注意

goctl rpc protoc 指令生成 rpc 服务对 proto 有一些事项须知:

  1. proto 文件中如果有 import 语句,goctl 不会对 import 的 proto 文件进行处理,需要自行手动处理。
  2. rpc service 中的请求体和响应体必须是当前 proto 文件中的 message,不能是 import 的 proto 文件中的 message。