既然有 HTTP 请求,为什么还要用 RPC 调用?

首先 RPC 框架配置复杂,如果走 HTTP 请求同样也能做到相同功能,而且配置可以几乎为零配置。项目也不用引用太多的包。虽然如果单纯用 RPC,可以不用类似于 Servlet 的 Web 标准。

这个回答里恰巧讲了一些rpc通信协议的细节,但是强调一遍通信协议不是rpc最重要的部分,不要被这个回答带偏了。如果要了解rpc请更多的去了解服务治理(soa)的一些基本策略,推荐去看看dubbo的文档。

这个问题其实是有理解误区的,首先 http 和 rpc 并不是一个并行概念。

rpc是远端过程调用,其调用协议通常包含传输协议和序列化协议。

传输协议包含: 如著名的 [gRPC](grpc / grpc.io) 使用的 http2 协议,也有如dubbo一类的自定义报文的tcp协议。

序列化协议包含: 如基于文本编码的 xml json,也有二进制编码的 protobuf hessian等。

因此我理解的你想问的问题应该是:为什么要使用自定义 tcp 协议的 rpc 做后端进程通信?

要解决这个问题就应该搞清楚 http 使用的 tcp 协议,和我们自定义的 tcp 协议在报文上的区别。

首先要否认一点 http 协议相较于自定义tcp报文协议,增加的开销在于连接的建立与断开。http协议是支持连接池复用的,也就是建立一定数量的连接不断开,并不会频繁的创建和销毁连接。二一要说的是http也可以使用protobuf这种二进制编码协议对内容进行编码,因此二者最大的区别还是在传输协议上。

通用定义的http1.1协议的tcp报文包含太多废信息,一个POST协议的格式大致如下

HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

<html>
  <body>Hello World</body>
</html>

即使编码协议也就是body是使用二进制编码协议,报文元数据也就是header头的键值对却用了文本编码,非常占字节数。如上图所使用的报文中有效字节数仅仅占约 30%,也就是70%的时间用于传输元数据废编码。当然实际情况下报文内容可能会比这个长,但是报头所占的比例也是非常可观的。

那么假如我们使用自定义tcp协议的报文如下

报头占用的字节数也就只有16个byte,极大地精简了传输内容。

这也就是为什么后端进程间通常会采用自定义tcp协议的rpc来进行通信的原因。

-- 分割线 2017.08.03 --

可能回答里面没有描述清楚,这个回答仅仅是用来纠正victory的回答的。所谓的效率优势是针对http1.1协议来讲的,http2.0协议已经优化编码效率问题,像grpc这种rpc库使用的就是http2.0协议。这么来说吧http容器的性能测试单位通常是kqps,自定义tpc协议则通常是以10kqps到100kqps为基准

简单来说成熟的rpc库相对http容器,更多的是封装了“服务发现”,"负载均衡",“熔断降级”一类面向服务的高级特性。可以这么理解,rpc框架是面向服务的更高级的封装。如果把一个http servlet容器上封装一层服务发现和函数代理调用,那它就已经可以做一个rpc框架了。

所以为什么要用rpc调用?

因为良好的rpc调用是面向服务的封装,针对服务的可用性和效率等都做了优化。单纯使用http调用则缺少了这些特性。

只要是远程调用都可以叫RPC,和是不是通过http没什么关系。

REST就是一种常用的rpc,M纪用的实现是OData,A纪用的是CoralService,都是基于http的封装。

另外你说http同样功能几乎零配置,那是胡扯。你要想达到同等级的通用性,扩展性,还加上一堆常用的feature如重试、限流、容错等等,自个配置好http差不多拿出来也是和这几个一个等级的框架了。你能这么说说明你对常用rpc框架提供的功能压根没有什么概念。

当然rpc也有不通过http的,可以直接走socket,或者其他协议,在不同的场景甚至有优于http的性能表现,这个很正常。用http不是因为它性能好,而是因为它普适,随便一个web容器就能跑起来你的应用。

首先,实名赞扬题主的问题。这个问题非常好

其次,实名反对各个上来就讲RPC好而HTTP不好的答案。因为,题主的观点非常对

HTTP协议,以其中的Restful规范为代表,其优势很大。它可读性好,且可以得到防火墙的支持、跨语言的支持。而且,在去年的报告中,Restful大有超过RPC的趋势

本想引用下报告内容,无奈最近由于某些原因,KeXueShangWang被Qiang了。等我日后出墙时,再做补充。

但是HTTP也有其缺点,这是与其优点相对应的。首先是有用信息占比少,毕竟HTTP工作在第七层,包含了大量的HTTP头等信息。其次是效率低,还是因为第七层的缘故。还有,其可读性似乎没有必要,因为我们可以引入网关增加可读性。此外,使用HTTP协议调用远程方法比较复杂,要封装各种参数名和参数值。

而RPC则与HTTP互补,我们详细介绍下。看完这篇回答,能让你对RPC的产生、原理、实现代码都有着清晰的了解。这样,也能在业务系统中,在RPC和HTTP之间做好抉择。

但需要再说一句,不是说RPC好,也不是说HTTP好,两者各有千秋,还在比拼中

要问我站谁?我根据业务场景,灵活站位……

评论区产生了一些争论,我在这里统一进行说明。争论主要发生在两点:

1、HTTP和RPC同一级别,还是被RPC包含?

2、Restful也属于RPC么?

对于以上两点,我画图来一一说明。

上图是一个比较完整的关系图,这时我们发现HTTP(图中蓝色框)出现了两次。其中一个是和RPC并列的,都是跨应用调用方法的解决方案;另一个则是被RPC包含的,是RPC通信过程的可选协议之一。

因此,第一个问题的答案是都对。看指的是哪一个蓝色框。从题主的提问看,既然题主在纠结这两者,应该是指与RPC并列的蓝色框。

第二个问题是在问远程过程调用(红色框)是不是包含了Restful(黄色框),这种理解的关键在于对RPC的理解。

RPC字面理解是远程过程调用,即在一个应用中调用另一个应用的方法。那Restful是满足的,通过它可以实现在一个应用中调用另一个应用的方法。

但是,上述理解使得RPC的定义过于宽泛。RPC通常特指在一个应用中调用另一个应用的接口而实现的远程调用,即红色框所指的范围。这样,RPC是不包含Restful的。

因此,第二个问题的答案是Restful不属于RPC,除非对RPC有着非常规的宽泛理解。

RPC的英文全称是Remote Procedure Call,翻译为中文叫“远程过程调用”。其中稍显晦涩的其实就是“过程”,过程其实就是方法。所以,可以把RPC理解为“远程方法调用”。

要了解远程过程调用,那先理解过程调用。非常简单,如下图,就是调用一个方法。这太常见了,不多解释。

而在分布式系统中,因为每个服务的边界都很小,很有可能调用别的服务提供的方法。这就出现了服务A调用服务B中方法的需求,即远程过程调用。

要想让服务A调用服务B中的方法,最先想到的就是通过HTTP请求实现。是的,这是很常见的,例如服务B暴露Restful接口,然后让服务A调用它的接口。基于Restful的调用方式因为可读性好(服务B暴露出的是Restful接口,可读性当然好)而且HTTP请求可以通过各种防火墙,因此非常不错。

然而,如前面所述,基于Restful的远程过程调用有着明显的缺点,主要是效率低、封装调用复杂。当存在大量的服务间调用时,这些缺点变得更为突出。

服务A调用服务B的过程是应用间的内部过程,牺牲可读性提升效率、易用性是可取的。基于这种思路,RPC产生了。

通常,RPC要求在调用方中放置被调用的方法的接口。调用方只要调用了这些接口,就相当于调用了被调用方的实际方法,十分易用。于是,调用方可以像调用内部接口一样调用远程的方法,而不用封装参数名和参数值等操作。

那要想实现这个过程该怎么办呢?别急,咱们一步一步来。

首先,调用方调用的是接口,必须得为接口构造一个假的实现。显然,要使用动态代理。这样,调用方的调用就被动态代理接收到了。

第二,动态代理接收到调用后,应该想办法调用远程的实际实现。这包括下面几步:

  • 识别具体要调用的远程方法的IP、端口
  • 将调用方法的入参进行序列化
  • 通过通信将请求发送到远程的方法中

这样,远程的服务就接收到了调用方的请求。它应该:

  • 反序列化各个调用参数
  • 定位到实际要调用的方法,然后输入参数,执行方法
  • 按照调用的路径返回调用的结果

整个过程如下所示。

这样,RPC操作就完成了。

调用方调用内部的一个方法,但是被RPC框架偷梁换柱为远程的一个方法。之间的通信数据可读性不需要好,只需要RPC框架能读懂即可,因此效率可以更高。通常使用UDP或者TCP作为通讯协议,当然也可以使用HTTP。例如下面的示例中,为了保证实现最简单,就用了HTTP进行通信。

讲到这里,RPC的产生原因、原理应该清楚了。为了让大家真的明白,我写了一个真的是最最简单的RPC实现。把它放到了:

https://github.com/yeecode/EasyRPC​github.com

它包含一个客户端,一个服务端。客户端只要调用自身内部的接口,就通过这个小的RPC实现调用到了服务端的方法。

下面是客户端的代码,看着类有点多,其实代码不长。其中的RPC代码完成完成动态代理、远程调用参数序列化、远程调用发起、远程调用结果反序列化的工作。

RPC客户端

下面是服务端的代码,代码更少,完成远程调用接收、调用参数反序列化、调用实际触发、调用结果序列化的工作。

RPC服务端

这样,一个RPC小框架就做完了,并不复杂。

所以,不要被RPC吓到,它就是让一个应用调用另一个应用中方法的一种实现方式。与调用远程接口区别不大,条条大路通罗马。

再说一次,不是说RPC好,也不是说HTTP好,两者各有千秋。本质上,两者是可读性和效率之间的抉择通用性和易用性之间的抉择。最终谁能发展更好,很难说。

要问我站谁?我根据业务场景,灵活站位……

如果还有什么没说清楚,可以留言讨论。

也可以关注我,偶尔出没解答架构设计和编程问题

有的时候我们嫌标准 HTTP 太慢/太复杂/不适合特定场景,那么就自己发明一个新的。

http 协议还是面向桌面浏览器的广域网的一个通信协议,在缓存、幂等重试乃至 Cookie 这种浏览器安全相关的方面做了很多功夫。网络环境不同可能有一些不同的协议冒出来,像现在面向移动网络,http2 在减少 RTT、优化长连接(吃我推送拉日活)之类做了很多功夫。

在数据中心网络里网络环境稳定带宽高延时低但是业务复杂,rpc 协议就主要在注册发现、监控、负载均衡、熔断降级方面上做功夫,重要的是标准化。rpc 协议也可以选择建在 http/http2 之上,有现成的 nginx、curl 之类工具可以用。

而基于http请求的方式,通过请求路径已经确定了,你要请求的服务的类,方法名,比我上面的说的方式更傻瓜化。

所以rpc为啥不http请求,取决于你的服务端支不支持http

另外,dubbo这样的rpc一般在公司内部系统使用,而对外的open API一般会rest或者http方式。因为dubbo这样的rpc会暴露服务地址,不安全,而http方式是基于域名,网络要更安全。

大体上来说,我觉得HTTP请求是非常浪费资源的。虽然其实本质上,HTTP也是走在TCP上面的,但是HTTP请求自己增加了很多自己的信息,因此会消耗带宽资源,以及服务器上的内存。还有一个问题,我们知道,HTTP协议其实是无连接的,因此基于这个原因,也让通讯变得更复杂了,而且人类理解起来也困难,典型的模型不匹配,就好像OO对象和数据库表一样。

那么相对来说,直接用socket就好很多,更符合代码级别的调用这个感觉。socket上走的字节,没有多余的数据,也方便压缩。而且socket的连接能够一直保持着,这也更符合代码级别调用的感觉。还有一点就是用socket如果在同一主机上,可以使用unix domain的通讯方式,直接使用IO,连端口占用都省了。再假如,你的架构里面用到了docker,那么这种通讯方式就能够方便的沟通docker内外了,非常方便。

所以RPC框架少有用HTTP的,多是使用socket或者消息队列。而且其实很多情况下自己就用socket或者消息队列实现了,连RPC框架都省了。当然RPC框架还有很有意义的,比如apache的Thrift。

第一,是先有的RPC,才有HTTP,而不是反过来。事实上HTTP 0.9也很像RPC,到1.1才稳定成今天这个样子,也就是REST风格。

第二,用HTTP和RPC框架各有优劣,没有绝对的好坏。对于性能需求高,开放度相对低,用RPC有它的优势。

http就是最朴素的rpc啊

你觉得rpc框架复杂,不是因为rpc复杂

是因为框架复杂

说实话,现在哪个框架不复杂呢?

rpc框架包含了重试机制,路由策略,负载均衡策略,高可用策略,流量控制策略等等能用在消息处理上的功能,当然复杂了

简单项目没需求的话,就用ngx+http就好了呀

不能拿你家排水和城市排水系统比吧

“既然有 HTTP 请求,为什么还要用 RPC 调用?”

rpc是可以包含http的呀。

“首先 RPC 框架配置复杂,如果走 HTTP 请求同样也能做到相同功能,而且配置可以几乎为零配置。”

rpc框架主要为了解决一些分布式场景下的问题,例如熔断机制、注册中心、负载均衡;走http请求几乎0配置?我想题主想问的是以spring cloud基于spring boot的0配置http restful的代表与当下分布式框架dubbo的区别吧?

这个要从背景说起了,在这些分布式框架热起来之前,soa被广泛接收,但是有些大厂开始走向云计算的道路时,延伸出“微服务”架构,这些分布式框架就成了微服务架构中的一环。

然后市面上两大主流分布式框架Spring cloud和dubbo作为两个分支争议很大。dubbo在先,但停更,后有当当dubbox扩展支持http但文档结尾指出安全性还不完善,然后阿里的dubbo要重启但表示安全性不会去考虑完善,因为自己内部有一套网关。

之前soa的时代,安全性问题很多是通过esb解决的,而到了微服务的时代很多人就面临安全性问题,大量时间里讨论前后端分离,json web token,token授权认证,oauth2,其困惑主要是安全性问题怎么解决。

由dubbo和dubbox的设计可以看出,这些厂在使用rpc框架于内部,而且仅限java,而暴露给互联网的肯定还是要http,这点可以从阿里他们家的淘宝开放平台文档里看出来。

Spring cloud率先形成生态圈,网关解决了安全性问题。

阿里的dubbo早期就占有大批量市场,很多大厂也在用,http网关(例如zuul)不支持dubbo的。大厂需要自己去找网关支持dubbo的那种,另外异构语言的系统也需要自行开发dubbo框架(例如在php下开发一套dubbo框架php版才能内部去远程调用,这对那中php/.net转java的公司非常不友好,没能力自己搞的话,往往都要在套一层web服务得意打通),所以dubbo作为一个rpc框架在当时的热度有点跟风了,非大厂很难把这些架构问题解决,毕竟国内也就那么几家大厂。

而当时Spring cloud基于spring boot的几乎0配置,加上全家桶,定位非常明确,就是给中小互联网公司玩的。

最后截止2019年,dubbo进入apache,形成一整套生态圈,上面这些问题也得意解决,现在中小企业如果还误用了当年遗留的dubbo,我建议在架构上可以作出调整,升级到Apache dubbo ,然后可以把一些异构系统也整进来,对外的接口网关也可以用起来,可以搜索Apache dubbo。

他们之间的区别是两个方向

SOA用ESB企业服务总线把不同协议的服务和消息统一走ESB的一种服务的协议,而微服务提倡整套架构采用统一的风格。

既然是统一的风格,那么他们之间的区别就是,一个是统一成restful,一个是统一成dubbo化。

Spring cloud采用了restful api统一微服务中的接口风格,这种风格原本就在中小企业里广泛使用。

dubbo则是底层socket,用在一些极致追求性能的场合,但整个生态要迁就它,例如公司内部一个部门搞java用dubbo,另一个部门搞.net,那么搞.net部门如果业务有依赖需要调阅dubbo,那么需要通过学习并且也用上http://dubbo.net版,否则搞java的部门需要另外开发出一套web服务并且为它另外搞一套负载均衡。

现在很多非盈利机构或半盈利机构,如行政服务中心、医院等,许多人也开始玩了分布式,很显然由于一家机构里会有好多套系统,并且来自不同的软件供应商,一旦核心系统上了dubbo,以后要信息打通就要招标文件上限定java语言并支持dubbo框架。但出于数据的安全,包括一些手机端/手持嵌入式设备/桌面应用程序的出现,这些也许并非java所擅长,最终还是要多搞一套,所以限定dubbo对这种机构来说也很不利的,会带来很多额外成本。

事实上我们这座城市是有很多号称从阿里回来的程序员,跟风微服务,忽悠给中小企业上dubbo,一个nginx能解决的问题一定要搞出一个分布式框架来,认为用了分布式框架就能不用nginx和网关,最后在web应用一层还是得上nginx(笑哭)。

我们这座城市那会还流行一种面试题,“分布式事务怎么办?”“断电断网了怎么办?”(笑哭)

本质是软件系统故障与恢复的问题,因为我们这座城市的领导和面试官很多非计算机课班出生,对这类基础问题时常缺乏正确判断,侥幸以为上个分布式框架能解决这些问题的心理。

我相信这也是很多公司上rpc框架为了解决一些分布式系统带来的问题,但有些公司是盲目地上的。

聊到rpc,分布式框架,微服务架构,也顺便提一下前后端分离。

这也是软件开发行业的一个通病吧,例如近年来流行起来vue框架,很多缺乏正确判断的领导想把实现前后端分离的重任寄托在vue框架,也是难为vue框架在官方介绍文档的一开头还要澄清一下“我不是前端框架,我只是ui框架”,言下之意就是前端范围好大我们不敢当,我们只是前端里某一层的框架。

这里说一下基于 HTTP 的 RPC,比如 XMLRPC。

它干了一件事,实际上就是把你的方法调用映射成 HTTP 请求。

如果你直接创建 HTTP API,服务端这边没啥费事的,但是客户端这边,你要写代码发送很多 HTTP 请求。以最流利的 HTTP 库来看,你要用参数构造 Request 对象,用 client 对象发送它,然后从 Response 获得响应正文,这些代码没办法省(因为 HTTP 客户端已经足够简洁了),你有多少个 API 就得写多少个。

但是如果你用 XMLRPC,你只需要把这些方法写进一个接口里,然后服务端客户端各存一份。把这个接口扔给服务器,让它找到方法的实际位置,把这个接口扔给客户端,让它返回一个 Mapper。然后你就能拿着这个 Mapper 调用服务器上的方法了。

总之这个东西跟 ORM(特别是 MyBatis)很像,都属于需要一些配置但是能省很多代码的东西。并且用的越多越省代码。

  1. Http 的话要先走dns 再走lvs 再走nginx 链路太长,可用性SLA指标太低
  2. http是文本协议比其他用在rpc上的序列化 二进制协议 如 thrift protobuf等来说性能太低 ,会造成应用rt太高。举个例子就是,在美团研发时做的分布式ID系统leaf,用thrift的rt在1ms之内平均只有0.4ms,单台机器QPS 5w。同时也提供了http的方式,QPS最多达到1w,rt在50ms左右
  3. 如果你的应用对SLA和rt要求都不高,怎么好用怎么用
  4. 有知有说http不能像调用本地方法那样和RPC一样,建议github搜索feign OpenFeign/feign

内部一般走RPC,因为方便写代码;外部一般走http,因为只有客户端和浏览器可以touch到用户。

http虽然通用性强,但协议本身太重,在某些调用频繁且对带宽限制比较严格的场景下,会显得很笨拙 

实际上很多所谓的rpc框架就是在tcp上自己封装的应用层协议。因为是定制的协议,所以不会有http协议那么臃肿。 

RPC协议一般有以下特点:

  1. 针对性设计封包、解包效率更高
  2. 舍弃协议层面的可读性提高编码效率
  3. 轻量无过载头部信息,HTTP的头信息规范太多
  4. RPC某些场景下可以采用UDP等其他协议,没有TCP的握手负担和延迟
  5. 如果使用底层二进制编码(非文本序列化XML、JSON等)进一步提高序列化和反序列化效率

http 是短连接,rpc 是长连接

使用rpc不必要每次都创建,释放socket链接

举一个例子:

最IM应用的时候,如果使用http请求,只能是一次request 一次 reply,因为是短连接,服务器没法直接给客户端发消息通知。

如果是rpc的话,由于一般是长连接,服务器就可以直接给客户端发通知了。

虽然http2也有长连接,rpc也有短连接实现,但是一般认为http是短连接,rpc为长连接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章