现如今,或多或少的 API 是基于 RESTful 风格的。em,你可以像我一样稍微思考一下这个 “REST” 意味着什么,但是 “REST” 在广义上的定义方向是没错的。“REST” 将会一直代表这个意思吗?明显不可能,接下来会怎么发展呢?
当我们谈论到 “REST” ,可以讲的通俗一点它就是一种基于 HTTP 形式的 API 。事实上,大量含有 URI 的 “REST” 形式的 API 都有提供 CRUD 操作,有些 URI 是在负载的时候就嵌入的,所以可以说 RESTful 是一开始就有的。但是现在我偶尔还会听到“CRUDL”这个名词,这个“L” 代表 List 。
当我在AWS工作时,我们常做的事是设计一个服务或者一款APP的数据层和它的控制层。举例,假定数据库像RDS服务一样。那么在app的控制层中你去创建,配置,备份,启动,停止和删除数据库。数据层主要内容是SQL语句,连接池和RDBMS包
非常有趣的是我们需要去注意的一点是控制层可以非常好的匹配RESTFUl风格的API,但是数据层就不是这样了。在数据库当中REST并非属于必要,(但是DynamoDB数据库的数据层是非常嵌合RESTFUL)。
我在想模式能否是这样,当你在控制层去删除和创建对象时,控制层可以很好嵌合大多数RESTFUL风格的API。数据层却完全不一样。要不是因为REST和控制层像是天造地设一样合适,我还以为不论什么想要替换掉REST都将从数据层开始。
RESTful 缺陷 我们想超越 REST 的原因可能有哪些?下面我列出了一些:
延迟
创建和销毁一个 HTTP 连接的每一个操作都不是没有代价的。虽然为了减小这种代价努力了几十年,但是它依然存在。
比如说两个我身边的朋友创建的消息服务的例子: Amazon SQS 和 MQ. SQS 已经运行了十多年, 每秒处理百万级的消息,而且如果你的消息发送者和接收者能合理平衡的话,可以做到出奇的快。甚至我听说过消息还没有发送就已经被接受的例子。其实是长轮询的接收者在发送端销毁 PutMessage 的 HTTP 连接之前就已经收到消息。一方面,它没有使用 HTTP,而是用了 TCP/IP 长连接和它自己的报文协议。所以可以得到惊讶的发送和接收延迟。但是另一方面,你的消息吞吐量受限于关闭这些连接的“message broker”所能处理的连接数。很多选择 MQ 的人的原因相信他们这么做的因为是不想用 RESTful 接口。
耦合 自然情况下,大部分 REST 请求是同步的。也就是说, 你调用一个请求得等着结果返回。现在 (使用HTTP术语)你的请求有可能会返回202 Accepted,在这种情况下,您可能希望发送一个 URI 作为 webhook 回调地址,或者可以响应中获取一个可以拉取信息的 URI。但是在所有这种情况下, 耦合是非常紧密的。 你必须维护一些请求的状态直到调用方处理完它,不管是立刻或者要等一会儿。
这是很糟糕的情况。特别是在微服务中一个请求方以超过服务方可以处理的频率调用时,一些不想看到的情况就会出现。
短生命周期(Short life) · 举例来说,citizen-ship 应用 ,它可能需要花费数周时间来编排大量的服务,偶尔还需要人工互动,但是它在处理一些请求上是毫秒级的,这样的情况让一个线程等待某件事发生的想法就变得荒谬了。
关于 GraphQL 。GraphQL 的存在主要是为了处理这样的一种情况,即一个客户端需要组合多种类型的信息来完成其工作。例如,一个手机应用程序就是基于 GraphQL 来构建一个信息丰富的显示器。由于 RESTful 风格的接口在处理单一资源的时候的优势,这可能就会导致大量请求的浪费。GraphQL 可以让你在单个请求的多个资源中任意选择字段。从上面可以推测,当服务器端响应一些简单的请求的时候,这个时候你只需要使用 GraphQL 去组装信息然后完成请求的响应就可以了。
我了解到大量的客户端开发人员都喜欢 GraphQL ,这似乎看起来它在编程的世界里有一席之地。但是我并没有看到 GraphQL 改变了我们的处理方式。首先,由于受限于 GraphQL 的语义的限制,这将不能让我们像客户端开发人员一样组装任意的查询去得到一个很好的效果。(公平的讲,SQL 也是一样的。)无论如何,在我们使用 GraphQL 去让同步的 API 运行的更有效率的时候,我看到了 GraphQL 便捷的特性。
关于 RPC 的话。这些天,我想我一定是指 gRPC 。长久的经历让我见证了一代又一代的 RPC 框架悲惨地失败。它们是脆弱的而且需要大量的配置还不能达到预期的性能。对我来说 ,能看到 RESTful 风格的 API 成功的更紧密地耦合起来真的很难 。但是我的观点不一定是正确的。
Post-REST: 消息和事件处理 · 这种方法已经普及,我的意思是在我所使用的云基础架构中已普及。其中的想法是你得到一个请求,校验之,也许对其进行一些计算,然后你把它放到队列(或总线,或流,或任何你想要称之的名称),那么忘记这些事情吧,它们不是你的问题了。 ¶
请求处理的下一阶段是由读取队列的服务实现,并将答复路由回原始请求者或将其传递给另一个服务阶段。现在为了使这个可工作,所以问题队列必须快速(这些天里它们是的),可扩展(它们目前是的),非常非常耐用(它们目前是的)。
这里有很多优势:首先,瞬态查询激增不再是问题了。此外,一旦你拥有一个消息流,你就可以进行扇出和过滤,汇编和子集以及各种其他有用的操作,而勿须干扰上游消息源的操作。
Post-REST: Orchestration · 这涉及到工作流程领域了,这是我近期一直在努力研究的的事情。 其中“工作流程”是指跟踪具有多个步骤的计算状态的服务,其中任何一个步骤可能需要任意长时间,可能会失败,可能需要重试,并且其行为和输出会影响后续输出步骤的选择及其行为。¶
越来越多(例如)Lambda函数,不再是提供请求和返回响应,而是在提供输入,等待其完成,并将其输出路由到更下游的工作流上下文中执行。
Post-REST: 持久连接 · 回到之前的几段,我谈到了MQ消息代理如何工作,维护了一堆固定的网络连接,并在它们之间来回提取字节。不难相信,有很多场景可以很好地适应此数据和执行的流动方式。
现在,我们已经在半路上了。例如,SQS客户端通常使用“长轮询”(通常约30秒)来接收消息。这意味着,他们请求消息,如果没有,服务器不会说“没有”,它会挂起该连接一段时间,如果有消息收到,则将它们发送给调用者。 如果你有一堆线程(可能在多个主机上)在长轮询SQS队列,你可以获得大吞吐量和低延迟,并真正降低使用HTTP开销。
接下来的两步也很容易预见。第一步是已经广泛部署的HTTP/2,它允许你在单个网络连接中复用多个HTTP请求。明智地使用之后,它可以为你带来使用永久连接的一些好处。但它仍然与TCP密切相关,这有一些不幸的副作用,在此我不会对其深入探讨,部分原因是这不是我所深刻理解的事物。但我希望看到诸多应用程序和服务可以从HTTP/2进化中获得很好的收益;在某些程度上,因为就客户端而言,他们仍在生成和响应与之前完全一致的HTTP请求。
之后的下一步是QUIC(Quick UDP Internet Connections,快速UDP互联网连接),它放弃了TCP而使用UDP,同时保留了HTTP语义。这已经在很多Google产品上投入使用。我个人认为这是一个非常重要的事情; HTTP如此成功的原因之一是它的连接是短时的,因此在工作时受到破坏的可能性要小得多。这非常棒,因为设计一种可以处理连接断开的应用程序级协议是非常困难的。在HTTP的世界中,你同时需要处理的多数情况是一些失败的请求,而连接断开只是可能发生此问题的原因之一。UDP通过没有真正的连接使连接断开问题消失了。
当然,没有免费的午餐。如果你正在使用UDP,你就不会在TCP中获得TC,我说的是Transmission Control,它负责打包和重组以及校验和检查、流控以及加载其他超级有用的东西。但从我看到的证据判断,QUIC做得足够好,足以完全支持HTTP语义,所以再一次,想要继续使用与2005年相同的旧的XMLHttpRequest调用的应用程序可以愉快地保持不变了。
未来! ·对于我来说似乎是不可避免的使用持久连接,特别是在高吞吐量高弹性云原生应用程序的场景中,我们将看到对持久连接,编排和基于消息/事件的逻辑的依赖性的稳定增长。如果你还没有使用这些东西,那么现在是开始学习的好时机。
但我敢打赌,在可预见的未来,所有服务请求的很大一部分将具有(近似)HTTP语义,而对于大多数控制平面和相当多的数据平面,REST 仍然提供了一种很好的分解复杂的方法问题,它的极端简单性和弹性意味着如果你想设计网络应用程序,你仍然需要学习这种思考方式。