五句话,
HTTP API就是以HTTP协议为承载的API。
RPC style API 一般是指以HTTP协议或其他协议来承载的调用远程方法的API。
REST是一种API架构范式,核心围绕着资源,交互的是资源的状态;
特点是响应结果可缓存,服务可发现;目标是API长期使用。
RESTful API是实现了不同REST约束的API,存在不同的成熟度级别。
GraphQL是Facebook推出的RPC API,提供类SQL查询语言,可查询指定数据。
笔者本身是系统架构师出身,近些年多从事数据科学相关工作。在目前面向框架的开发模式下,本无意讨论架构问题。但所遇之人,从上到下无不经常把微服务和REST接口挂在嘴边,似乎REST的接口和微服务就是解决一切问题的不二选择。
个人认为需要理清相关概念和其内在含义,特写此博客。
首先我们需要明确一下API(Application Programming Interface),即应用编程接口。软件系统暴露给其他软件或计算机系统的可使用的功能接口,一般来说,它会定义如何使用、数据格式和需要遵循的惯例等等。例如POSIX API等。
而HTTP API是目前比较流行的通过Web进行功能交互的接口,可理解为Web Service Over HTTP。就是以http协议为承载的web服务功能接口。
RPC (Remote Procedure Call),远程过程调用,即计算机程序调用一个执行在不同的地址空间(通常在其他计算机上),但是从代码层面上来看却不需要对远程交互进行显示编码,看上去就像该调用运行在本地一样。从形式上看是一种客户机-服务器模式,典型实现方法是请求/响应消息传递系统(Request-Response Message Passing)。
RPC经常被用于分布式计算系统,在面向对象语言中通常表示为RMI(Remote Method Invocation)。
RPC的核心可以理解为是一种进程通信的形式(Inter-process communication, IPC),最典型的RPC应用就是NFS(Network File System)。
以上所提及的RPC的概念与互联网似乎没有什么关系,实际上如果把RPC这种方式以http或者AMQP作为承载就是我们常说的web service的Web api。在调用一个远程过程时,我们需要指定方法名称,参数并接受返回结果。数据格式也可以自定义,常用的方式有
XML-RPC:保证XML中的数据类型比较困难
JSON-RPC
SOAP(Simple Object Access Protocol):结合XML-Schema和WSDL(Web Service Description Language),相对难以使用。Saleforce是使用这个方案的典型案例。
如果以http协议作为承载,就是把方法写在URL中,参数写在query string或者body中。
举个例子:
POST /Hello HTTP/1.1
HOST api.test.com
Content-Type: application/json
{"name": "nobody"}
其对应javascript函数为:
/* Signature */
function Hello() {
// ...
}
/* Usage */
Hello("nobody")
另外一个值得一提的RPC实现就是Google所推出的gRPC,可以理解为一种更为简单和现代化的"SOAP",其使用的数据格式为ProtoBuff,需要数据模式定义,类似于SOAP的WSDL。
看上去RPC已经很好了,但为什么又会出现REST这种东西,它是什么,试图解决什么问题呢?这个就是这一小结尝试解释的。
REST(Representational State Transfer),即表示状态转移,是Web服务的一种架构范式,是由Roy Fielding在2000年的论文中首次提出。而符合这种范式的API设计,我们称为RESTful API。
在阐述REST这种范式的核心理念之前,我们需要简单聊一聊Web的产生和变革。
在Web的世界里,任何机构和个人都可以将其资源和内容以html的形式发布在互联网上,通常会有服务器来处理和响应请求。这就带来了一个重大变革:把中心化的信息和内容获取形式转变为分布式的。
随之而来的是可以在自己的网页中引用其他人的内容,且内容的形式从文本发展为图片、语音、视频和混合的种种形式,我们称之为Hypermedia(超媒体),它是即非线性网状结构对块状多媒体信息进行组织和管理。
信息通过网络互联互通形成了今天的互联网,这种方式大大缩短了人、信息、实体三种角色之间的距离。搜索引擎的出现又一次使整个过程加速,提高了效率。所以,笔者认为互联网的关键词是内容、互联、超媒体。
抛开互联网的商业模式不谈,每个网站都希望自己的内容被更多的人看到和访问到。随着互联网用户的爆炸式增长,几乎每个网站的内容都在互联网上被重复的传播的。这带来了一个问题,就是文档的位置不是最优的,因为需要重复传播。一个相对简单和成本较低的解决方案就是把html文档缓存在距离用户近的地方。但是这显然是不够的,因为用户对内容的需求不仅仅限于html文档,包括所有超媒体;形式也不限于文本获取,而是各种web 服务。
为了解决此类问题,HTTP工作组开始构思更为良好的解决方案,其成员Roy Fielding推出了一种更为广泛的架构范式,REST。它与HTTP/1.1是相互独立的,但是HTTP/1.1是实现REST的一个良好开端。HTTP1.1与此相关的规则为:
客户端从服务器申请文档
服务器是无状态的,所有的状态由客户端来处理和维护
客户端可以缓存结果以备未来使用
客户端可以缓存结果对于web的扩展性非常关键。让我们来看一个具体例子,假如你有一个歌曲非常受欢迎,很多人都从你的ftp服务器上下载这个歌曲。中间节点的服务器只能在你的允许之下才可以缓存这个文件。
于是你使用apache web server建立了网站,并用html建立了指向这首歌曲的超链接。然后你设置apache web server以HTTP/1.1中的Cache-Control头为public来发送这首歌曲。现在中间节点都可以缓存这首歌曲而不需要向你询问,从很大程度上减少了网络开销。
REST的核心准则之一就是响应是否可以缓存。HTTP/1.1通过Cache-Control来实现这一点。
从REST本身而言,并没有任何约定必须由HTTP协议来承载,也就是说采用FTP,SNMP,SMTP等等都是可以的。
对比于RPC风格接口针对于某个方法,REST接口是针对于某个资源或者资源的集合的;而对于HTTP协议而言,它的基础组件是超媒体(Hypermedia)。
REST的核心概念是HATEOAS,(Hypermedia As The Engine Of Application State),概念上可以理解为超媒体的控制。
而超媒体简单来看,实际上提供的是资源的状态和“下一个可选的动作”,而这个动作是一个链接。举一个我们购物的例子,对于一个购物清单票据:
{
"data": {
"type": "invoice",
"id": "001",
"attributes": {
"created_at": "2020-05-12 12:12:01",
"sent_at":""
}
},
"links": {
"pay": "https://api.pay.com/invoices/001/payment_attempts"
}
}
REST为一个响应提供了所有相关信息,客户端可以选择可选动作来做出正确的回应。
客户端不需要预先知道API的任何细节和使用方法。
所以REST就是对资源的表示的状态的转移,包括资源的转移和动作的转移。
除了REST需要捆绑资源或者超媒体以外,其他约束包括:
Client-Server:客户-服务器模式
Stateless:无状态
Cacheable:可缓存
Uniform interface:接口唯一
Layered System:分层系统
这些约束条件的最主要目的就是使接口可以长期存在和被使用。
随着REST的概念逐步被大家所接受,从2006~2014年,很多互联网服务声称其提供REST API,但实际上很多提供的只是RPC加上http动词和URL而已。这样的话,这些接口的响应结果是不能缓存的,也没有提供可选的下一步动作;同时,也很难提供REST的附加客户端解耦功能。
前面也提过,REST并没有规定其承载协议,目前大多数使用的还是HTTP协议。另外一个思路是采用AMQP(Advanced Message Queue Protocol),但由于其实现难度,所以到目前为止还没有具体的实现方案。两个比较广泛使用的REST API标准为:
OData(Open Data Protocol)
JSON-API
最后,我们需要说一下所谓的RESTful API。正如前面所提到的,RESTful API只是对REST范式的实现而已,但不同的API对REST范式的约束的满足程度不一样,Richardson 成熟度模型定义了符合REST的四种级别:
0 – 以HTTP暴露API方法,需要提供参数以调用
1 – 暴露资源而不是方法
2 – 正确使用HTTP动词
3 – 以对象暴露超媒体,且使部分或者全部的API是可被发现的
按照REST的作者Fielding的要求,必须满足成熟度级别3才可以被称为RESTful API;如果不能全部满足,称为"progmatic REST",就是实用的REST。
实际上按照RESTful风格来设计接口是比较困难的,因为你需要把复杂的事情用非常简单的方式来表示,以CRUD的方式来描述所有功能,这是非常有挑战的。
GraphQL实际上是属于RPC的,同时它也吸取了REST/HTTP中的一些好的思路。它提供了一种查询语言,有点类似于SQL。可以使用GraphQL来指定资源和属性,只返回指定的内容。
{
classmate {
name
friends {
name
}
}
}
//Request
{
"data": {
"classmate": {
"name": "DJ",
"friends": [
{
"name": "Mar Kuier"
},
{
"name": "Li Quene"
}
]
}
}
}
//Response
上面展示了GraphQL请求和响应的例子。
对于C(Creates), U(Updates), D(Deletes)操作,GraphQL提供Mutation操作。
众所周知,GraphQL是由Facebook推出的,在最近几年发展迅猛,使用者越来越多。它主要解决了客户端和服务器端无用数据交互的问题和提高接口重用性。早些时候,Facebook还推出过FQL,这是一种在GET操作上使用类似SQL的语法进行查询的方式,这显得有些怪异。GraphQL可以看作是FQL的前置,它与传输层协议无关,可以融入AMQP或其他。
从上例中,我们可以看到“classmate"的”name"返回了字符串,而"friends"则返回了列表。
REST的出现是存在其背景和根源的,它的设计是非常出色的。但它并不是接口设计的唯一选择,它也有其局限性和不足之处。
下一次将与大家分享关于架构模式方面的一些话题。