写点什么

[大厂实践] Chick-fil-A 的服务 API 流程实践

作者:俞凡
  • 2023-09-23
    上海
  • 本文字数:3805 字

    阅读完需:约 12 分钟

本文介绍了美国快餐连锁巨头 Chick-fil-A 在技术团队中引入 Buf 和 Connect 解耦 API 依赖并实现了良好的 API 定义模式。原文: Connect(ing) Chick-fil-A

背景

2018 年,Chick-fil-A 的客户技术团队遇到了一些 API 问题。该团队擅长构建 API,这些 API 可以实现一些了不起的事情,比如将客户订单从手机通过迷宫般的网络发送到销售点的遗留系统,但与这些 API 交互需要团队之间的大量协作。更准确的说,我们遇到的是 API 契约问题。团队很少有明确、完备的合约。大多数合约要么缺乏文件记录,要么完全不明确。这在我们电子商务项目最初几年的创业环境中可以预料。我们团队最初在 2016 年推出了移动应用和电子商务平台,由于营销团队的努力以及新冠疫情的影响,电商业务从很小的一部分增长到销售额的重要部分。


随着业务越来越成熟,缺乏明确的、记录良好的合约就成了一个问题。前端开发人员经常需要直接联系后端开发人员,以确定某个值是否应该作为字符串或整数传递,或者某个字段是否真的需要。后端开发人员要么依赖记忆,要么必须深入研究源代码,以了解后端在做什么、需要什么、什么类型等等。这是一个显而易见的问题。随着团队数量增加,情况变得越来越糟。我们的目标是优化以"X-as-a-service"模式运行的团队,这种模式只需要团队之间有限的合作就可以集成。相反,我们发现所有团队都在"协作"交互模式下操作(参见: 团队拓扑)。

改变

所以你可能会想:"为什么不把合约文件写得更好呢?"公平的说,我们当然是这样做的。我们从团队编写简单的 markdown 文件开始,这些 markdown 文件以直接从Basecamp API文档中提取的方式深入介绍他们的 API(感谢Jason Fried)。这对我们来说很有效,特别适用于拥有技术分析师的团队,这些技术分析师可以帮助消除开发人员必须清理历史文档的一些负担。然而,这个过程并没有应用于各个团队,并且严重依赖于人的记忆来确保文档保持最新。举个例子,我们的 Location API 团队有一个强大的技术分析师来保存文档,但是许多团队没有相同的技能。这一尝试帮助我们将几个关键 API 做得更好,但给团队增加了许多开销,并且仍然可能出现不可接受的人为错误。


尝试了几个月之后,我们渴望找到一个更适合我们组织的方法。我们开始寻找其他选择:


  • 靠近代码(Proximity to code): 这是必须的。文档离代码(真实来源)越远,它在脆弱曲线上的位置就越远。我们发现,脆弱的文档可能会导致比单独依赖协作更糟糕的结果。


随时间变化的文档脆弱性


  • 功能所有权(Functional ownership): 如果技术后端系统的产品所有者拥有的"经验"是 API(强调接口),那么他们的客户是前端团队和其他后端团队,而不是传统的 Chick-fil-A 客户。因此,为了更好的为客户服务,这些 API 团队的 SLO 应该是基于可测量的东西,比如 P95 延迟和可用性,而不是像开发人员体验和易于集成这样难以测量的目标。但是,如果产品负责人对这些"模糊"的东西缺乏可视性,那就没有真正的所有权。将它们从代码中抽象出来,放到产品负责人可以可视化的视图中,他们就可以更好的负责整个产品。

  • 契约优先的规范接口定义(Contract-first, canonical interface definitions): 这是迄今为止我们所尝试的最不一样的东西。我们需要引导契约,而不是简单的从后端实现的类中派生。在处理隐式契约时,很容易发生复制传递,例如前端团队认为客户标识符字段命名为customerId,而实际上后端团队将其命名为userId。哦,不是说我们做过这种蠢事,但理论上可能会发生。如果我们能够推动接口定义规范化,那将是一大步。生成的 SDK 是首选,这样就无法引入错误。


有了这些启发,我们开始评估几种技术,这些技术已经帮助数百个组织解决了同样的问题。

GraphQL

GraphGQ 有很多让人喜欢的地方。这是一项引人注目的技术,具有许多附加功能,特别是如果 API 是基于 Node.js 构建的时候。因为我们的 API 主要用 Java 和 Go 构建,所以工具支持并不像 Node.js 那样完善,而且服务器到服务器调用的人机工程让人感觉不舒服。此外,当我们可以同时控制前端和后端实现时,灵活性所带来的好处就不那么有价值了。

OpenAPI 规范

OpenAPI 几乎征服了我们,我们用在 DTT(数字转换和技术,Digital Transformation & Technology)部门的其他部分。我们已经通过 Springfox 使用了 Swagger。然而,我们并没有充分利用 Swagger 注释,这使得我们的文档感觉平淡无奇。零星注释也会让代码感觉有点混乱,并把所有责任都推给开发人员去更新。这可以通过团队流程和整体期望来解决,但没有给 API 功能的所有者足够的控制权。

gRPC

gRPC 是 Google 设计的一个远程过程调用框架,有一些非常引人注目的优点。我们可以从包含接口和服务的一些基本定义的 protocol buffer 文件开始,在Service中定义 schema,并使用Message定义接口。总的来说,这是一种相对简单的语法,比 yaml 更容易阅读。


gRPC 有很多让人喜欢的地方:


  • ✅通过生成的代码定义规范化接口。

  • ✅靠近代码,能够在实现 API 的同时提交契约。

  • ✅业务所有权,具有易于理解的语法,允许业务人员理解并为契约做出贡献。


看起来是全方位的胜利。


直到我们开始在概念验证之外使用。然后对话就成了这样:


"protoc",这个 CLI 是由一些业余人员构造的吗?哦,等等,不,它是由谷歌构造的。为什么这么笨重?不知道。好吧,我们能解决。等等,我究竟如何获得生成 Java 代码的"protoc"?好的,明白了,通过 Maven 插件,这似乎不同于其他语言使用"协议"的方式。现在我们可以生成一些代码,让我们把它放到 Spring API 中。哦,这里的支持似乎有点问题。让我们在 Go API 中试试。



最后,我们用 Go 语言构建了一个服务,并在环境中运行。


好吧,我们把一些外部流量引到这东西上。嗯,AWS 中对 gRPC 的 ALB 支持是全新的,而且没有很好的文档。让我们看看能做些什么来让这个跑起来。哦,天哪,我们终于从前端客户端获得了流量!欧耶。


退一步说,让所有网络节点都支持 gRPC 并不是一件容易的事。最重要的是,gRPC 建在一个有围墙的花园里,我们不能用以前的中间件,不能用 curl,不能用常规调试代理,也不能用相同的 HTTP 库。我们被困住了。所有被吹捧的好处都换来了糟糕的开发者体验,这并不是一个理想的权衡,但我们仍在继续努力。在生产环境中运行后,我们最终得出结论,尽管我们喜欢 Protobuf 带来的许多好处,但无法忍受 Protobuf 和 gRPC 为前后端团队带来的糟糕的开发体验。

Connect

最后,我们遇到了一个更新的技术产品,Connect。那是多么美好的一天啊。当时,Buf 有一个漂亮的 CLI 工具,承诺比"protoc"更快,更重要的是,比竞争对手提供了更好的人体工程学。他们也有一个疯狂的愿景,让 Protobuf 的整个世界变得更好、更干净、更容易使用,而不是一个有围墙的花园。当然,他们兑现了这一承诺。



Buf 最终实现了将世界Connect起来的宏伟愿景。Connect 是一个允许三种交互模式的协议:


  • gRPC 互操作性: 向后兼容 gRPC 客户端和服务器(如果用作客户端)。

  • HTTP POST + Protobuf: 提供了 Protobuf 的序列化优势,并充分理解了 POST(或者现在可选的GET)请求的本质。

  • HTTP POST + JSON: 提供 JSON 的可见性,同时仍然具有强制的、可检测破坏性更改的契约。对于可追溯性远比延迟重要的低优先级环境,是一个完美的选择。


使用 Connect 和 Buf 使我们能够采用一种对我们来说非常有效的流程,并使我们远离"协作"模式,更接近"X-as-a-service"交互模式。


整个流程是这样的:


  1. 团队在 Protobuf 中定义 API 模型和契约。在开发新 API 时,Proto 文件可以保留在分支中。该分支每次被推送到 Github 时都会自动同步到 Buf Schema Registry 作为"草案"。允许前端客户端或后端消费者在"测试"模式下生成代码,而合约仍在开发中。如果需要的话,还可以允许团队并行化工作。

  2. 发起 Pull request,供接口的负责团队和消费团队进行审查。这一阶段将运行破坏性变更检测并验证检查规则。到目前为止,破坏性变更检测是我们最喜欢的好处,确保了 API 的前向和后向兼容性。后端团队可以放心发布产品,因为他们知道合约变更不会影响到消费者。

  3. 一旦团队对契约感觉良好,就会被合并到实现 API 团队的主分支中。然后与 Buf Schema Registry 同步,并允许消费团队查看 API 文档并使用生成的代码。

  4. 重复以上步骤。


Buf + Connect 给了我们 Protobuf 和 gRPC 所承诺的许多好处,而且没有任何缺点。我们的许多团队现在都采用"契约优先的 API 设计",极大改善了我们构建 API 的方式和团队交互方式。

总结

随着 Chick-fil-A 客户技术团队的成长,我们经历了沟通渠道数量的指数级增长,因为团队依赖于紧密"协作"作为交互模式。很明显,我们需要努力使团队能够在可能的情况下对需求实现自助服务,以"X-as-a-service"的方式运作。这使我们发现了 Protobuf 以及 Buf 在其Buf Schema Registry (BSR)Connect中提供的工具。这些工具帮助团队朝着提供自助交互模式的方向发展,并帮助我们在 Chick-fil-A 数字商务的复杂世界中优化构建、记录和集成 API 的方式。


如果只是运营一个小团队,我们可以忍受团队互动的低效,因为沟通矩阵小而简单。然而,随着组织规模扩大,应该强烈考虑对 Protobuf、Buf 和 Connect 等工具进行投资,这些工具有助于提供一种媒介来简化团队交互模式。




你好,我是俞凡,在 Motorola 做过研发,现在在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!

发布于: 刚刚阅读数: 4
用户头像

俞凡

关注

公众号:DeepNoMind 2017-10-18 加入

俞凡,Mavenir Systems研发总监,关注高可用架构、高性能服务、5G、人工智能、区块链、DevOps、Agile等。公众号:DeepNoMind

评论

发布
暂无评论
[大厂实践] Chick-fil-A的服务API流程实践_云原生_俞凡_InfoQ写作社区