AfterShip 是一家服务国际电商赛道的 B2B SaaS 公司,自 2012 年成立。近几年业务持续高速增长,主要业务包括提供全球物流订单追踪系统、退换货系统、邮件营销、商品推荐等电商 SaaS 解决方案。
在近日,AfterShip 与 GoCN 联合举办的 GopherDay 技术交流会上,AfterShip 高级研发工程师 Li-Hang 进行了《从 0 到 1 搭建十亿级包裹 API Versioning》的主题分享。
以下是根据现场演讲所编辑的技术文章,希望对大家有所帮助。
1. Why
1.1 SaaS 产品的特点
SaaS 产品是提供某一领域的解决方案的软件服务,本质是提供订阅服务。
国内大家常用的有飞书、钉钉、企业微信、腾讯会议、百度云、QQ 音乐等基于订阅制的都可以归类为 SaaS,国外常用的 SaaS 有 Google Doc、Zoom、Notion、Slack 等产品。
AfterShip Tracking 属于国际 SaaS 中的 B2B2C 产品,商家订阅 AfterShip 的服务,提供订单信息给我们,我们为商家提供包裹追踪服务,商家可以从我们这获取到物流更新反馈给他们的消费者,也可以通过我们的一体化服务为他们的消费者提供通知等服务。
SaaS 2B 和 2C 的差异
-
产品定位 - 2C 产品通常满足个人消费者的便利和娱乐等日常需求;2B 产品主要是支持企业关键业务流程和运营的重要工具。
-
稳定性影响 - 如果 2C 服务不稳定,会对个人消费者的使用体验造成影响,但不一定会对企业造成损失;如果 2B 服务不稳定,可能会导致业务中断影响到企业的正常运营,造成企业严重的损失。
-
稳定性承诺 - 2C 业务一般企业不会对个人消费者有太多稳定性的承诺;2B 业务在合同中包含 SLA 条款则很常见。SLA 用于规定服务提供商与客户之间的服务水平标准和责任;通常包含了服务的可用性、响应时间、故障处理和解决时间等关键指标,以确保服务的质量和可靠性。
国内 SaaS 和国际 SaaS 的技术规划的差异
-
专注和极致 - 国际化 SaaS 产品在细节打磨上会有更多的投入,包括对于产品的交付质量有更高的要求,尤其是产品还在早期时候也普遍有较高的质量和标准化。
-
规范和标准 - 体现在如 API 的定义,普遍都是遵循 RESTful API 标准,并且都有相对规范的 API 设计和定义。
-
安全合规 - 在全球范围内,尤其是欧盟、美国和中国在安全隐私要求上都日趋严格,也都有出台了对应的条例:GDPR、CCPA 和《个人信息保护法》。另外对于一家 SaaS 公司来说,当所服务的客户群体有更多是大客户时,对于安全隐私方面的要求也会更高。
-
全球化 - 设计全球化的系统,相比面向单一国家/地区的系统,会需要关注更多的维度。在做系统设计时,系统应该预先设计为支持多国家/地区、多语言、多时区和多币种等。
1.2 API 的重要性
API 也是产品的一部分,而不仅仅是技术。对于 SaaS 产品,提供 API 服务几乎是与企业级用户合作的必需项,特别是在海外。
-
自动化 - 通过 API,企业可以利用 SaaS 产品的功能来构建自动化流程和工作流程。
-
定制化 - 通过 API,企业可以开发自己的应用程序、插件或集成其他工具,以满足特定的业务需求。
-
数据集成 - 通过 API,企业可以灵活地与 SaaS 提供商实现数据的共享和交换。
1.2.1 标准与规范
AfterShip 的 API 严格循序标准与规范,做到极致的细致和严谨,主要包括 RESTful、全球化的架构设计和安全性。
1.2.2 AfterShip API 的演进路线
AfterShip 从十万级发展到到十亿级的包裹追踪,从没有 API 到高标准的 API,不断拔高标准的 API 设计,有力地支撑了业务的增长。
1.3 没有 API Versioned 时面临的挑战
挑战一、变更不规律,打乱客户更新计划
随着 AfterShip 的客户越来越多、品牌越来越大,要求越来越严格,对我们的挑战越来越大。有些客户反馈,原来一个月的通知时间太短了,你们应该提前三个月通知,更有甚者表示没有计划配合我们对已有的 API 进行任何变更。
挑战二、新功能发布缺少调试环境
研发们都知道,在我们对接新的接口协议时,除了对接接口文档一般都有一个测试环境联调的过程。一方面我们要求客户配合变更,虽然我们有提供对接文档,但我们线上只有一个版本的 API,缺乏“测试环境”,用户根本没有办法调试。
2. What
为了解决上述的问题,我们开始学习行业内的知名公司是怎么做的。像 Github, Stripe, Shopify 和 Microsoft 这些公司的 API,都有 API Versioning 的概念。
2.1 本质
很多同学可能以为 URL 上带上 v1, v2, v3 这样的版本号字眼就是 versioning,其实不是。从 2014 年开始我们的 API 就带上了 v4 的版本号,但一直都没有实现 Versioning。
API Versioning 的本质在于
-
API 有版本概念。
-
同一个版本的每次 API 变更都能向后兼容,向后兼容 - API 的稳定性要求避免破坏性的更改。在进行更新或修改时,应保持对现有功能的支持,以防止影响已部署的应用程序或客户端。
-
不能向后兼容的发布只会发生在新版本中。
怎么评判一个 API 能不能做到向后兼容呢?
我们来看看 Github 关于 API Versioning 的介绍。
所有做 API Versioning 的企业,都会对 breaking changes 有一套自己的规范。不同企业会因为业务形态不同因此对 breaking changes 有不同的定义,有些企业可能比较严格,有些企业可能比较宽松,但大原则是如果变更可能影响到客户端已有的集成即认为该变更是 breaking changes。
只有做到对 API 不发布任何 breaking changes 才能叫向后兼容。
2.2 实现效果
一个非 Versioned 的 API 就像我们刚才提到的,所有功能都在一个版本上迭代,就会遇到我刚才提到的问题。
一个 Versioned 的 API,将提供多套稳定的 API 在线上,每套 API 有独立的逻辑、实例、资源,各版本之间互不影响。客户端可以自由选择调用哪个版本。
举个例子,对于刚才提到的问题,如果用户在使用 2023-10 这个版本,此时我们需要发布新功能,我们新发布的内容只会在 2024-01 的版本发布,完全不会影响到 2023-10 这个版本。
当新版本发布后,用户在生产环境上可以继续使用 2023-10 的版本,同时他们把 2024-01 这个版本当作他们的“测试环境” 进行联调,实现对他们的业务零 downtime 升级。
3. How
3.1 Support Policy
通过技术调研,我们发现企业新增一个版本都会带来额外的维护成本,所以版本发布频率高的企业,版本支持时间较短;版本发布频率低的企业,版本支持时间较长。企业可以根据自身的业务特性选择合适的 Support Policy。
AfterShip 的 Support Policy :
1、版本发布周期:三个月定期发布
2、版本支持时长:十八个月
3.2 API version
版本指定方式如图所示:
业界主要有通过 request header 和 URL path 两种指定 API version。我们考虑通过 URL 这种方式更清晰更好维护所有采用这种方式。
3.3 Webhook version
指定版本:对于 Webhook 的版本选择有些不同,Webhook URL 由用户提供,在 URL 中指定版本有些不现实。业界的主要方式是当用户在配置 Webhook URL 的时候需要指定一个版本,后续 webhook 将以指定的版本向该 URL 发送。
确认版本:通过接收到的 webhook request header 确认,header as-webhook-version 即表示该 webhook 的版本。
3.4 公开文档
文档页面包含所有 live 的版本
Migration guide:每个版本有对应的指引引导用户如何从上一个版本升级到当前版本。
3.5 系统架构
3.5.1 Overview
API 请求流程
用户的 API 请求首先会经过我们的外部网关身份、权限、Rate limit 等验证,然后根据不同的 URL 把请求分发到不同的 API 版本应用。应用层把请求处理后转发到能力层,能力层与数据库、消息队列等组件进行交互完成请求。
Webhook 推送流程
当数据发生更新时,有一个消息分发器负责监听数据总线。根据数据的用户归属找到对应的 Webhook 配置,根据 Webhook 配置上的版本信息把消息分发对应 webhook 版本的应用进行处理。
3.5.2 Trade off
External API 这一层属于应用层。运行的是对外的 API,每个应用对应一个 API 版本,这种设计是为了让每个版本的运行环境隔离,发布新版本时肯定不会影响其它版本。每个版本有其特有的 Versioning 逻辑,如对应版本的 URL 和 header,也有其特有的版本业务逻辑。
Internal API 这一层属于能力层。运行的是领域 API,每个应用大多对应一个领域,为上层应用提供能力。在这一层没有 API 版本的概念,每个 API 依赖一个数据库。为什么?成本问题。假设最简单的情况:有 5 个领域 API,每个支持 6 个版本,就需要维护 30 个应用;但实际上我们的系统架构还要远比这复杂,每个 API 还将依赖其它更底层的 API、消息队列、数据库等资源。如果要完全做到版本化,这里需要维护的版本依赖将是个很可怕的数字。作为 trade off,我们只在应用层做版本化,每个版本维护的业务逻辑实际上是对能力层的兼容。
3.6 版本维护
代码:不同 API 版本的代码维护在同一个代码仓库,但每个 API 版本有一个独立的代码分支。基于代码分支构建镜像,以不同镜像发布不同版本的应用。
自动化测试:为每个 API 版本设计其独立的测试用例,在每次发布前检查每个版本是否存在行为变更。
监控看板:根据每个版本的流量特性添加独立的监控规则,如果存在某个版本存在比较特别的业务逻辑也可能要增加特定的业务指标。
4. Takeaway
我们总结了一些经验供各位直接借鉴:
-
API 不仅仅是技术,也是一款产品。如果想要和企业用户合作,API 是必需项。在一开始就应该以高标准、高要求规划我们的 API。
-
API Versioning 的本质是最大化解决向后兼容问题,已存在的 API 不会出现向后不兼容的变更,不能向后兼容的功能只会在新版本中发布。
-
根据我们业务迭代的速度,决定是按固定周期发布版本还是按需发布新版本。比如我们平均每三个月发布一个新功能,可以考虑以 3 个月为周期发布版本,每个版本保留 12 个月,那我们同时最多只需要维护 4 个版本的 API。
-
建议在应用层实现 Versioning,不建议在能力层实现。虽然可以做到非常极致,但是业务复杂后维护成本也是相当可怕。
当然,如果你也想在一家工程师氛围浓厚的国际 SaaS 公司工作,那么欢迎你加入我们。