討論交流
我的两分钱 2023-01-08 130 0 0 0 0
程序员,程序开发,微服务,今天,REST随着微服务的兴起而大行其道,但REST和RPC到底有什么区别?通过HTTP+JSON进行的RPC是不是就是REST?HTTP和JSON不过是函数/服务调用过程中的通讯和数据协议,而RPC…

今天,REST随着微服务的兴起而大行其道,但REST和RPC到底有什么区别?通过HTTP + JSON进行的RPC是不是就是REST?

HTTP和JSON不过是函数/服务调用过程中的通讯和数据协议,而RPC从来也没有对具体的通讯和数据协议进行过约束,REST和RPC到底有什么本质区别吗?

让我们来看看REST的提出者Roy Fielding本人的回答:

I am getting frustrated by the number of people calling any HTTP-based interface a REST API. ... In other words, if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period.

Roy Fielding

但是什么叫做"application state driven by hypertext"? 

我想再多的理论上的解释也不如一个例子说得清楚。

假设我们有一个员工管理系统,员工有三种类型:合同工(Contractor)、固定合同期的员工(FixedTerm)、终身雇佣的员工(Permanent),员工可以晋升(Promote)到不同类型,如下图所示:

然后,员工管理系统对外提供了一系列API,通过这些API可以查看员工的基本信息、晋升员工等。

晋升员工时不能违反上图的晋升规则(比如不能晋升一个已经是Permanent的员工为Permanent),这个业务规则是一个服务端的逻辑,但如果客户端不知道这个业务规则,就可能进行错误的调用(或给用户呈现错误的UI),所以很多时候就出现了客户端、服务端的逻辑耦合,当有一天,服务端修改规则时,比如不允许Contractor直接晋升为Pernament,客户端也必须跟着修改。

让我们用程序说话,首先通过API查询员工:

curl -v host:port/employees

得到如下返回结果:

{
"_embedded" : {
"employeeList" : [
{
"id" : 1,
"name" : "Bilbo Baggins",
"type" : "Contractor"
},
{
"id" : 2,
"name" : "Frodo Baggins",
"type" : "FixedTerm"
}
]
},
...
}

然后我们调用API来晋升1号员工:

curl -v -X PUT host:port/employees/1/toPermanent

得到如下结果:

{
"id" : 1,
"name" : "Bilbo Baggins",
"type" : "Permanent"
}

我们看到该员工已经被成功晋升,如果我们对该员工重复上述晋升:

curl -v -X PUT host:port/employees/1/toPermanent

我们将得到一个HTTP 405的错误码,以及相应的错误信息:

...
< HTTP/1.1 405
< Content-Type: application/problem+json
...
{
"detail" : "An employee of type 'Permanent' can't be promoted to type 'Permanent'",
"title" : "Method not allowed"
}

客户端为了避免这样的错误,将不得不耦合服务端逻辑,在进行晋升调用前,首先检查员工类型,只有符合规则才进行调用。而且,当有一天,业务规则调整为不允许直接晋升Contractor为Permanent,如果客户端没有及时跟着修改,上面例子中首次晋升调用就会失败。

我们再看看"application state driven by hypertext"是如何解决这个问题的。

还是通过API查询员工:

curl -v host:port/employees

得到如下结果:

{
"_embedded" : {
"employeeList" : [
{
"_links" : {
"Promote_To_FixedTerm" : {
"href" : "host:port/employees/1/toFixedTerm"
},
"Promote_To_Permanent" : {
"href" : "host:port/employees/1/toPermanent"
},
},
"id" : 1,
"name" : "Bilbo Baggins",
"type" : "Contractor"
},
{
"_links" : {
"Promote_To_Permanent" : {
"href" : "host:port/employees/2/toPermanent"
},
},
"id" : 2,
"name" : "Frodo Baggins",
"type" : "FixedTerm"
}
]
},
...
}

我们看到,这次不但返回了员工信息,还把在当前状态下,能对员工进行的操作明确地返回了。REST是"Representational State Transfer"的缩写,这里就是Representational一词的体现。

我们把1号员工晋升到Permanent:

curl -v -X PUT host:port/employees/1/toPermanent

得到如下结果:

{
"id" : 1,
"name" : "Bilbo Baggins",
"type" : "Permanent"
}

我们看到该员工已经被成功晋升,更重要的是,我们同时看到对该员工已经没有可执行的晋升操作了。

假设我们是把1号员工晋升到FixedTerm而不是Permanent:

curl -v -X PUT host:port/employees/1/toFixedTerm

得到如下结果:

{
"_links" : {
"Promote_To_Permanent" : {
"href" : "http://localhost:8080/employees/1/toPermanent"
},
},
"id" : 1,
"name" : "Bilbo Baggins",
"type" : "FixedTerm"
}

我们看到,该员工被成功晋升为FixedTerm,且现在只能进行晋升到Permanent的操作了。

在上面的例子里,我们看到服务端状态的迁移清晰地体现在了API返回的数据中,客户端不再需要耦合服务端的逻辑。这也就是"Representational State Transfer"中 State Transfer的体现。

除了服务端状态的迁移,hypertext还可以解决客户端硬编码资源地址的问题,这里不做展开,如果有兴趣网上可以迅速找到答案。

所以REST和RPC的根本区别是什么?相信现在你已经有了答案。


Tag: 程序员 程序开发 微服务
歡迎評論
未登錄,
請先 [ 註冊 ] or [ 登錄 ]
(一分鍾即可完成註冊!)
返回首頁     ·   返回[討論交流]   ·   返回頂部