扫码订阅《 》或入驻星球,即可阅读文章!

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go学习
  • Go学院

    • Go小课
    • Go视界
    • Go小考
    • Go实战
  • Go资源

    • 优质课程
    • 在线宝典
    • 资源下载
    • 帮找资源
训练营 🔥
  • Go体系课&实战训练营
  • 升值加薪陪跑训练营
Go求职
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
  • 求职服务

    • 内推互助
    • 求职助力
    • 内推公司
Go友会
  • 城市
  • 校园
推广返佣
  • 返佣排行
  • 返佣规则
  • 推广学院
实验区
  • Go周边
  • Go宝典

    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
更多
  • 用户中心

    • 我的信息
    • 我的返佣
    • 我的消息
  • 玩转星球

    • 星球介绍
    • 星主权益
    • 吐槽专区
    • 成长记录
  • 合作交流

    • 商务合作
    • 讲师招募
    • 生态伙伴
author-avatar

GOLANG ROADMAP


首页
Go学习
  • Go学院

    • Go小课
    • Go视界
    • Go小考
    • Go实战
  • Go资源

    • 优质课程
    • 在线宝典
    • 资源下载
    • 帮找资源
训练营 🔥
  • Go体系课&实战训练营
  • 升值加薪陪跑训练营
Go求职
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
  • 求职服务

    • 内推互助
    • 求职助力
    • 内推公司
Go友会
  • 城市
  • 校园
推广返佣
  • 返佣排行
  • 返佣规则
  • 推广学院
实验区
  • Go周边
  • Go宝典

    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
更多
  • 用户中心

    • 我的信息
    • 我的返佣
    • 我的消息
  • 玩转星球

    • 星球介绍
    • 星主权益
    • 吐槽专区
    • 成长记录
  • 合作交流

    • 商务合作
    • 讲师招募
    • 生态伙伴
  • Go工程化规范设计

    • 前言
    • 开源规范
    • 文档规范
    • 版本规范
    • Git规范
    • 目录结构
    • 编码规范
    • 代码测试
    • 性能分析
    • API 设计
    • 项目管理
    • 研发流程
    • 参考资料
  • Go工程化标准实践

    • 前言
    • 项目结构
    • API 设计
    • 配置管理
    • 模块管理
    • 测试
    • 参考资料

扫码订阅《 》或入驻星球,即可阅读文章!

API 设计


Kyle

# RESTful API

一般用于处理对外业务。

# URI

  • 资源名使用名词复数表示。资源分为 Collection 和 Member 两种。
    • Collection:一堆资源的集合。比如系统里有很多用户(User), 这些用户的集合就是 Collection。Collection 的 URI 标识应该是 域名/资源名复数, 比如 https:// iam.api.marmotedu.com/users。
    • Member:单个特定资源。比如系统中特定名字的用户,就是 Collection 里的一个 Member。Member 的 URI 标识应该是 域名/资源名复数/资源名称, 比如 https:// iam.api.marmotedu/users/admin。URI 结尾不应包含/。
  • URI 中不能出现下划线 _,可用中杠线 - 代替(统一一种即可)。
  • URI 路径用小写。
  • 避免层级过深的 URI。超过 2 层的资源嵌套较乱,建议将其他资源转化为 ? 参数,比如:/students?school=qinghua&class=rooma 以 Query 参数代替 /schools/tsinghua/classes/rooma/students/zhang Path 参数。

当某些操作不便于映射为某个 REST 资源:

  • 将操作变成资源属性,比如暂时禁用某个用户:/users/zhangsan?active=false。
  • 将操作当作是资源的嵌套资源,比如 GitHub 加星:PUT /gists/:id/star。
  • 以上都不能解决问题,则可以打破规范。比如登录操作 /login。

# HTTP 方法

常用 GET(获取)、PUT(替换)、POST(新增)、DELETE(删除),其中 PUT 表示替换资源,不建议使用。

注意 安全性(不会改变资源状态,GET)和 幂等性(执行多次效果等价,GET、PUT、DELETE)。

关于批量删除,建议在操作路径中带多个用分隔符分隔的 id,比如:DELETE /users?ids=1,2,3 。可避免发送多次请求(可根据实际情况选择实现)。

# 返回路径

RESTful API 会向外界开放多个资源的接口,返回格式要保持一致;每个接口都会返回成功和失败两种消息,格式也要保持一致。

否则客户端代码要适配不同接口的返回格式,每个返回格式又要适配成功和失败两种消息格式,增加用户的学习和使用成本。

返回格式没有强制标准,可根据实际业务需要返回不同格式。

# 错误码

错误码需要附带业务标识,且对内对外分别展示不同的错误信息(不必对外暴露内部信息)。

建议在 HTTP Status Code 根据错误类型设置,并在 Body 附带详细的的错误信息(简明扼要,统一大写开头,不带 .)。

业务码可覆盖场景:基本错误、数据库类错误、认证授权类错误、加解码类错误。参考 iam/error_code_generated.md (opens new window),示例:100101

  • 10:服务代号。
  • 01:功能模块代号。
  • 01:功能模块下错误码序号。

HTTP Status Code 参考 HTTP 协议规范即可,一般以下几个即可满足需求:

  • 200:请求成功执行。
  • 400:客户端错误。
  • 401:认证失败。
  • 403:授权失败。
  • 404:资源找不到,可以是 URL 或 RESTful 资源。
  • 500:服务端错误。

可使用自定义的错误包以支持业务错误码,参考 marmotedu/errors (opens new window)。

# API 版本

通常将版本标识放置在:

  • URL 中,比如 /v1/users。最直观,GitHub、Kubernetes、Etcd 都采用的做法。
  • HTTP Header 中,比如 Accept: vnd.example-com.foo+json; version=1.0。
  • Form 参数中,比如 /users?version=v1。

# API 命名

驼峰命名法(serverAddress)和蛇形命名法(server_address)需要切换输入法,会增加操作的复杂性,也容易出错。

建议用 脊柱命名法(server-address),参考 GitHub API。

# 分页 / 过滤 / 排序 / 搜索

  • 分页:比如 /users?offset=0&limit=20,可减少 API 响应延时、避免返回太多条目,导致服务器 / 客户端响应慢,甚至导致服务器 / 客户端 crash。
  • 过滤:如果不需要资源全部状态属性,可在 URI 参数里指定返回的属性,比如 /users?fields=email,username,address。
  • 排序:可在 URI 参数中指明排序参数,比如 /users?sort=age,desc。
  • 搜索:建议按模糊匹配来搜索。

# 域名

建议采用 iam.api.marmotedu.com,支持 marmotedu.com 域名下扩展另一套域名系统,比如:storage.api.marmotedu.com、network.api.marmotedu.com。

# RPC API

一般用于处理对内业务。

# gRPC

具备以下特点:

  • 调用方便:屏蔽了底层的网络通信细节、调用方便:ClassName.ClassFuc(params)。
  • 无需打包和解包:RPC 调用的入参和返回的结果都是 Go 结构体,简化了调用步骤。

支持 4 种服务方法:

  • 简单模式(Simple RPC):客户端发送一次请求,服务端响应一次数据,rpc SayHello (HelloRequest) returns (HelloReply) {}。
  • 服务端数据流模式:客户端发送请求,服务器返回数据流响应,客户端从流中读取数据直到为空,rpc SayHello (HelloRequest) returns (stream HelloReply) {}。
  • 客户端数据流模式:客户端将消息流发送给服务器,服务器全部处理完成后返回一次响应,rpc SayHello (stream HelloRequest) returns (HelloReply) {}。
  • 双向数据流模式:客户端和服务端都可同时相互发送数据流,rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}。

与 RESTful API 的对比:

# SDK

即由服务提供者或其它组织或个人提供的、封装后端服务 API 的软件包,通常包含相关的库、文档、使用示例、封装好的 API 接口和工具。

命名方式:xxx-sdk-go / xxx-sdk-python / xxx-sdk-java 等。

目录结构:

  • README.md:帮助文档,包含了安装、配置和使用 SDK 的方法。
  • examples/sample/:使用示例。
  • sdk/:SDK 共享包,封装最基础的通信功能。如果是 HTTP 服务,基本是基于 net/http 包封装。
  • api:如果 xxx-sdk-go 只为某一个服务提供 SDK,可以把该服务的所有 API 封装代码存放在 api 目录下。
  • services/{iam, tms} :如果 xxx-sdk-go 中 xxx 是一个组织,这个 SDK 很可能会集成该组织中很多服务的 API,可以把某类服务 API 封装代码存放在 services/<服务名>下,如 AWS 的 Go SDK。
├── examples            # 示例代码存放目录
│   └── authz.go
├── README.md           # SDK使用文档
├── sdk                 # 公共包,封装了SDK配置、API请求、认证等代码
│   ├── client.go
│   ├── config.go
│   ├── credential.go
│   └── ...
└── services            # API封装
    ├── common
    │   └── model
    ├── iam             # iam服务的API接口
    │   ├── authz.go
    │   ├── client.go
    │   └── ...
    └── tms             # tms服务的API接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
├── examples                        # SDK 的使用示例
├── Makefile                        # 管理 SDK 源码,静态代码检查、代码格式化、测试、添加版权信息等
├── marmotedu
│   ├── clientset.go                # clientset 实现,包含多个应用,多个服务的API接口
│   ├── fake                        # clientset 的 fake 实现,用于单元测试
│   └── service                     # 按应用进行分类,存放应用中各服务 API 接口的具体实现
│       ├── iam                     # iam 应用的 API 接口实现,包含多个服务
│       │   ├── apiserver           # iam 应用中,apiserver 服务的 API 接口,包含多个版本
│       │   │   └── v1              # apiserver v1 版本 API 接口
│       │   ├── authz               # iam 应用中,authz 服务的 API 接口
│       │   │   └── v1              # authz 服务 v1 版本接口
│       │   └── iam_client.go       # iam 应用的客户端,包含了 apiserver 和 authz 2 个服务的客户端
│       └── tms                     # tms 应用的 API 接口实现
├── pkg                             # 存放一些共享包,可对外暴露
├── rest                            # HTTP 请求的底层实现
├── third_party                     # 存放修改过的第三方包,例如:gorequest
└── tools
    └── clientcmd                   # 用来帮助创建 rest.Config 配置的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 设计思路

通过 Config 配置创建客户端 Client,例如 func NewClient(config sdk.Config) (Client, error),在配置中可以指定:

  • 服务的后端地址:服务的后端地址可以通过配置文件来配置,也可以直接固化在 SDK 中,推荐后端服务地址可通过配置文件配置。
  • 认证信息:最常用的认证方式是通过密钥认证,也有一些是通过用户名和密码认证。
  • 其他配置:例如超时时间、重试次数、缓存时间等。

创建的 Client 是 struct 或 interface。建议使用 interface,可以将定义和具体实现解耦。Client 的每个方法对应一个 API,比如:

type Client struct {
    client *sdk.Request
}

// req 中可指定 HTTP 请求方法、路径、消息体。
func (c *Client) CreateUser(req *CreateUserRequest) (*CreateUserResponse, error) {
    // normal code
    resp := &CreateUserResponse{}
    // 发起 HTTP 请求:c.client 是 *Request 类型。
    // 可根据传入的请求参数 req 和 config 配置构造出请求路径、认证头和请求 Body,
    // 并调用 net/http 包完成最终的 HTTP 请求,将返回结果 Unmarshal 到传入的 resp 结构体中。
    err := c.client.Send(req, resp)
    return resp, err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 公有云厂商的 SDK 设计

参考 marmotedu/medu-sdk-go (opens new window)。

  • API 层构建客户端实例,并调用客户端实例提供的方法来完成 API 请求,每个方法对应一个 API。API 层最终会调用基础层提供的能力来完成 REST API 请求。
  • 基础层通过依次执行构建请求参数(Builder)、签发并添加认证头(Signer)、执行 HTTP 请求(Request)三大步骤完成具体的 REST API 请求。

# client-go 风格的 SDK 设计

参考 marmotedu/marmotedu-sdk-go (opens new window)。

具备以下特点:

  • 大量使用 Go interface 特性,将接口的定义和实现解耦,支持多种实现方式。
  • 接口调用层级与资源的层级相匹配,调用方式更友好。
  • 支持多版本共存。

# 命令行工具

大型项目中配备命令行工具(比如 kubectl、istioctl、etcdctl),可通过在脚本中调用 实现自动化,且通过将应用的功能封装成命令和参数,方便运维、开发人员在 Linux 服务器上调用。

待续。

  • RESTful API
  • URI
  • HTTP 方法
  • 返回路径
  • 错误码
  • API 版本
  • API 命名
  • 分页 / 过滤 / 排序 / 搜索
  • 域名
  • RPC API
  • gRPC
  • SDK
  • 设计思路
  • 公有云厂商的 SDK 设计
  • client-go 风格的 SDK 设计
  • 命令行工具