近年来,微服务架构发展迅速,SparkPost 就是早期落地微服务架构公司之一,他们发现落地微服务过程中,不光需要考虑服务发现、服务注册、服务调用跟踪链等等架构问题,也需要重视微服务 API 的变更管理。微服务的一大特性就是独立发布,快速迭代,但前提是足够稳定,他们在使用微服务构建API的过程中就遇到很多问题:
- 客户(微服务使用方)经常反馈 API 升级变更后不可用,有时影响范围不可控,导致该微服务上线延期,甚至线上故障,违背了微服务初衷。
- API 参数变化或返回结果变化而导致客户端行为不一致,依赖客户端需要大量重构,团队不能专注在创新型工作。
- API 易用性差, 使用方技术栈不统一,各自进行 API 抽象及封装,容易出错。
- 缺少文档及使用引导,需要大量支持工作。
- 闭门造车,产出微服务往往不能满足需求,运行一段时间就会逐渐废弃。
SparkPost 经过多年的探索与实践,总结了大量最佳实践,指导他们构建持久稳定的微服务 API。现如今,它们的 API 被成千上万的客户使用,包括Pinterest、Zillow 和 Intercomto,每月发送超过150亿封电子邮件。
在这篇文章中,我将回顾几个选择和最佳实践。
RESTFUL 是最好的,但要实用,不需要学究式
首先,也是最重要的一步,我们采取的步骤是决定使用 REST 作为 API。我们的理念是选择以下三个要素作为我们的 API 的基础:
1.HTTP : 这包括响应代码和操作符。操作符包括 POST、GET、PUT 和 DELETE,它们可以映射到基本 CRUD(创建、读取、更新、删除)操作。
2.EESOURCES : 这些是 HTTP 操作人员执行的实体。
3.JSON (JavaScript 对象表示法) : 这是一种通用的数据交换格式。
这三个元素提供了实用 REST API 所需的一切,包括简单性、可移植性、互操作性和可修改性。在构建了 API 之后,用户可以轻松地对其进行集成,而不考虑他们的编程语言,包括 C#、PHP 和 NODE.JS, JAVA,甚至是 SHELL 中的 CURL。他们可以不用担心潜在的技术发展,包括多种微服务。
当我们创建 SparkPost API 时,我们试着不要太过学究式地使用纯粹的 REST 模型,而是选择易于使用。下面是两个可能不遵循 RESTFUL 最佳实践的示例:
1.GET /api/v1/account?include=usage
2.POST/api/v1/sending-domains/example.domain.com/verify
第一个示例使用查询字符串参数来过滤实体中返回的内容。在第二个示例中,我们在终端名称中使用“VERIFY”这个动词,这可能不符合 RESTFUL。我们会讨论每个新的用例,并尽力确保它的一致性和易于使用。
发展进化并管理变化
我们有许多开发人员和团队在使用我们的 API 的微服务,并在持续的变更。当工程师确定它已经通过了我们的测试时,我们就会自动将变更部署到生产中。
我们很早就决定让我们的 API 在使用惯例和如何管理变更方面保持一致。我们建立了一个治理小组,其中包括代表每个团队的工程师、产品管理组的成员和 CTO。这个组建立了并强制我们遵守的 API 约定,并且是完全文档化的。
文档化的约定让我们可以减少不一致,并且更容易定义每个新的端点。以下是我们建立的一些约定:
- 在单词命名时,URL 路径是带有连字符的小写字母,并且区分大小写。
- URL 查询参数和 JSON 字段也是小写的下划线,并且是大小写敏感的。
- 请求主体中的非预期查询参数和 JSON 字段应该被忽略。
治理组还为如何进行更改以及允许哪些类型的更改设置了基本规则。有一些很好的 API 更改对用户是有益的,并且不会破坏它们的集成,包括:
- 一个新的 API 资源、端点或现有资源上的操作。
- 一个新的可选参数或 JSON 字段。
- 在 JSON 响应主体中返回的新字段。
相反,一个破坏性的变化包括任何可能破坏用户集成的东西,比如:
- 更改字段的数据类型。
- 一个新的必需参数或 JSON 字段。
- 删除现有端点或请求方法。
- 现有资源方法的实质性行为差异,例如将选项的默认值改为“TRUE”
做任何修改时不要制造破坏
即使它们是修复 BUG 或不一致的结果,也应该避免发生修改。通常在这种特殊的情况下运行比破坏与客户端的集成风险更大。如果变化是多样的,我们会非常谨慎,寻找其他方法来实现我们的目标。有时可以通过简单地允许用户通过帐户设置或 API 参数更改其行为来实现。
然而,总会有一种情况引入变化对我们用户的利益胜过任何潜在的不利因素,将引入的变化。但是在这些情况下,我们遵循了这些最佳实践:
- 我们分析了 API 日志,以了解更改可能会影响多少用户。
- 我们给用户至少30到60天的提前警告。
- 我们发了一封邮件或发表了一篇博客文章,里面包含了关于改变的详细信息以及我们为什么要做这些改变。
- 我们在 API 文档中提供了升级指导。
“一个版本”规则
在过去的三年里,我们对 API 进行了数千次的修改,现在仍然是第一个版本。我们很早就决定不将 API 的版本超过第一个版本,因为这样做会增加不必要的复杂性,从而减慢用户对我们最新和最强大功能的使用。对 API 的版本控制也会减缓开发和测试,让监控变得复杂,让用户文档变得混乱。
另外,我们的 API 没有版本控制,这意味着我们可以避免围绕主题的争论。有三种方法可以实现 API 的版本,所有这些都有潜在的缺陷:
- 把这个版本放到 URL 中: 容易做,但是从语义的角度来看是一个不好的选择,因为这个实体在 v1 和 v2 之间没有变化。
- 添加一个自定义的标题 : 也很容易做,但是语义不强。
- 在 ACCEPT 标头中放置这个版本: 语义强但是最复杂的方法。
使用客户端库来帮助非 JAVASCRIPT 用户
我们的一些用户更喜欢 PYTHON、C#、JAVA 或 PHP 而不是 JAVASCRIPT。我们通过维护客户端库(为其代码提供易于使用的函数库)将API集成到应用程序中,使其快速进行集成。
随着时间的推移,我们的客户库已经发生了变化,我们也做了相应的版本。我们已经了解到,在包装一个不断增长的 API 时,抽象是很困难的,所以我们专注于提供一层薄薄的抽象,并使用一些语法快捷方式来简化我们 API 的使用。这样做可以让我们的用户快速地访问我们任何 API,并且具有许多灵活性
“文档优先”的策略
我们将我们的文档视为代码,并在编写或更改一个 API 代码行之前使用它来记录我们的 API 更改。这样做可以帮助我们执行我们的约定,使所有事情保持一致,并保持良好的客户体验。它还削减了支持成本。
我们在 GitHub 中维护我们的文档,这使得技术和非技术用户可以很容易地做出更改。我们还发现,更容易审查变更的方式。我们使用 API Blueprint Markdown 格式和 JEKYll 生成 HTML 文档,以及一个名为 Algolia 的强大搜索服务。这样做让我们能够提供更好的客户体验,包括移动设备。
对于那些不想“滚动升级自己”文档的人来说,我们推荐 OPENAPI(以前称为“Swagger”)、Apiary和 API Blueprint。避免使用不适合 REST API 文档的工具是很重要的。我们建议在文档中包含一个亮橙色的“在 POSTMAN 中运行”的按钮,这样可以很容易地试用一个 API,以及成功和失败场景的例子。
听取用户的意见
最后,我们建议所有开发人员注意他们的用户的反馈。SparkPost 有一个社区 Slack 的频道,成千上万的用户可以方便地联系我们的产品、支持、工程和执行管理团队的成员。我们也有一个专门的开发人员关系团队,他们专注于与开发人员社区的合作。所有这些都让我们能更好倾听用户的意见,并将他们的反馈整合到我们的 API 中。
总结
随着微服务架构的发展,微服务快速增长,有的企业内部运维了超过1000的微服务,且仍在不断增长,每个微服务包含数十 API,如何持续管理微服务 API 变化将成为企业的关注点,SparkPost 根据这些规则和最佳实践,为他们的业务从提供现场电子邮件基础设施到以完全基于云计算的电子邮件发送服务提供了坚实的基础。
原文地址:http://blog.51cto.com/jfrogchina/2088413