我们应该如何构建明天的API呢?

2020-08-31 23:54:41

当我在2000年代中后期开始编写代码时,RESTAPI非常流行。几年前,罗伊·菲尔丁(Roy Fiding)发表了他关于设计基于网络的体系结构的热门论文,这篇论文迅速流行起来,而且理所当然:它的核心是鼓励构建统一的、共享的界面,以降低复杂性并使界面变得更简单。该行业正迅速从SOAP转向REST,因为后者使用户感觉更自然地组合他们特定行为所需的流。REST对于主要是服务器到服务器的流量来说,工作得非常好--现在仍然是如此,这在当时几乎覆盖了整个互联网。像Rails和Django这样的框架使得在几分钟内构建一个REST API成为可能,开发了几十个HTTP库,只需几行代码就可以通过网络进行交互,像curl这样的软件使得在几秒钟内就可以轻松地测试一个API。

从那时起,应用程序环境发生了戏剧性的变化,反过来也开始影响我们的API的设计和塑造方式。第一款iPhone于2007年发布,紧随其后的是2008年的App Store,开始了向客户端密集型界面的逐步转变。移动平台发展迅速,随之而来的是单页面应用程序(SPA)和渐进式Web应用程序(PWA)。在2010年发布后,Backbone.js作为首批帮助开发人员构建SPA的框架之一越来越受欢迎。像Ember.js、ANGLING JS和Final Reaction and Reaction Native这样的框架也加入进来,继续强调构建更大的SPA和PWA客户端。这种强调开始迫使API中的更改在一个请求(而不是多个请求)中返回客户端需要的所有数据,以减少往返并改善观察到的数据延迟。

此外,互联网的使用和访问在全球范围内呈爆炸性增长,自2010年以来,许多地区都出现了指数级的增长。毫不夸张地说,这种访问分布已经改变了世界,并要求开发人员考虑以前所未有的方式扩展他们的应用程序。越来越有必要考虑应用程序和开发人员体验对于远离您的数据中心的人来说会是什么感觉。

尽管这些变化具有变革性和巨大的规模,但我们构建API的方式相对保持不变。这还不错-如果说有什么不同的话,那就是它说明了REST能够很好地解决一个主要问题,以及它的原则如何引起开发人员的共鸣。然而,它确实提出了一个问题--随着所有新技术的开发和所有新用户的上线,我们的API需要在什么时候进行调整?我们需要做些什么来确保开发人员继续取得进展?为了在今天和明天提供绝对积极的开发人员体验,我们的API-以及它们背后的思想-很可能需要做出改变。

很难忽视数学(或科学):互联网流量的全球变化,加上物理定律的现实,将开始挑战我们如何构建API。很简单,我们在不断增长的距离上传输数据的速度是有限制的,这反过来意味着,如果我们不相应地考虑这一点,我们的一些或所有用户可能会有不太理想的体验。

具体来说,假设南非开普敦的一个用户向位于华盛顿州西雅图的数据中心发出请求。这大约是一次32000公里的往返旅行,以光速飞行大约需要100毫秒。加上跨地区的不同ISP之间的跳数、SSL握手和多次反弹以同步数据,您可能会开始添加数十秒。

对于如何处理这些限制,没有明确的“正确”答案,但个人和公司已经开始探索允许他们的API围绕这些限制进行扩展的解决方案。在需要跨全球移动数据的场景中,我们应该如何考虑API的数据一致性?我们是以延迟换取强一致性,还是转向最终一致性?到目前为止,API默认具有很强的一致性:您发布一个新资源并获取列表端点,因为更新发生得很快,所以可以保证它在那里。但是,如果您不能再为用户提供这种保证会发生什么,API应该如何改变以适应这种情况呢?

为了解决这些复杂性,Google创建了Spanner,这是一个在全球范围内提供强大一致性和广泛复制的数据库。Spanner使用名为TrueTime的自定义API实现,它利用使用GPS、原子钟和光纤网络的自定义硬件为数据复制提供一致性和高可用性。当然,它仍然不能解决在两个遥远的位置之间传输数据所需的时间,但在人类能够开发出自己的Ansible(或者除非您在Google Scale上操作)之前,我们将不得不在一致性和权衡之间进行权衡

当我们考虑API的数据一致性问题时,我们可以通过采用实时流或更多的异步流来抢占一些体验降级的先机-比如不一致的数据或缓慢的请求。异步流在面向客户端的应用程序中已经变得更加普遍,但是它们还没有广泛传播到API中。与其阻塞用户等待数据处理或同步,我们可以构建更多的异步流来实时推送状态更新,从而减少请求并更早地与数据进行交互。

本着类似的主动精神,我们可以更好地通知开发人员事件,最小化对API的请求和观察到的端到端延迟吗?从历史上看,开发人员要么轮询新数据,批处理请求以批量加载更改,要么使用WebHook查看是否有任何更改。WebHook已经存在很久了,可以让API将事件推送给用户,但是使用它们可能很有挑战性,并且是围绕HTTP概念构建的,这可能会限制拥有双向事件通道的全部潜力。发布双向流API可能会带来哪些新体验或新产品?原生流式API是否可以通过提供更无缝的更新逻辑来更好地适应实时同步?而且,如果我们追求它们,我们会把实现负担放在哪里-开发人员应该构建队列来处理数据,还是应该让API过滤更多的消防管?

随着程序化工作流变得更加复杂,即时处理数据变得更加困难。发展推式模型来取代我们已经习惯的拉式模型将变得越来越有必要使开发人员与变化保持同步。与真实世界的交互是很慢的-有许多阻塞流需要人工交互(这总是比单独的计算机慢,尽管我们人类可能会这样做),需要上传或下载平面文件进行处理的过程,甚至需要在工作时间以外停止处理的计算机系统。那么,我们是否要转向公开表示真实世界状态的状态机的API呢?如果是,我们应该如何优雅地揭露随之而来的复杂性?用户应该如何验证复杂的状态机或通过测试来表示它?

随着API复杂性的增加,并不是所有用户都需要相同的功能集,可定制的API或瓷器包装器可以帮助开发人员简化他们的应用程序。自定义是一个相当广泛的主题,从字段选择和自定义API结构到使用请求链或异步进程的自定义处理程序构建自定义流。今天,大多数API不会以定制的方式向用户提供太多内容,因为它可能需要大量的前期投资。尽管如此,API定制并不是一个新概念,JSONAPI提供稀疏字段集,Graph QL提供字段选择作为查询语言的一部分。在这里,允许更多的定制肯定会让开发人员将实现细节和逻辑的责任转移到API上。广泛采用,有时互联网连接的巨大差异意味着开发人员必须考虑其请求的效率,而不断添加的新产品增加了API的复杂性,因为他们试图遵守REST。让开发人员更多地控制他们的接口,使他们能够根据需要精确地调整他们的请求,而不是发送多余的数据-或者发送的数据不够多。

有很多未经利用的机会来适应和重新构建我们的API,以适应未来的世界。我怀疑我们将长期扎根于REST和HTTP-它们仍然是开发人员的关键工具,在许多情况下为构建简单、模块化和有目的的API提供了完美的体验。这些对开发人员来说很大程度上是无处不在的-构建这些API是许多人的第二天性,有无数的产品、工具和资源使它们的集成既灵活又基础。但是,随着我们互联环境的变化,我们开始超越许多久经考验的模式,修补破损的接缝,发现自己拥有拼凑在一起的开发体验。当我们考虑要保留什么和要改变什么时,我们需要记住,我们是为未来的开发人员构建的。如果我们愿意适应,我们可以构建API,使他们在未来几年取得成功。