最后更新:2022-07-29 17:43:21 手机定位技术交流文章
我记得我在工作时,第一次接触RPC协议,然后我非常困惑,我使用HTTP协议很好,为什么使用RPC协议?
于是就到网上去搜。
许多解释似乎非常正式,我相信所有平台的人都见过它们,解释,而不是表面上解释,使用一个我们不知道的概念来解释另一个我们不知道的概念,知道的人不需要看,不知道的人看或者不知道。
这种看了,又好像没看的感觉,云里雾里的很难受,我懂。
为了避免强烈的丑闻厌倦感,我们今天将尝试一种不同的讲话方式。
作为一个程序员,假设我们需要向A计算机上的进程发送数据,然后向B计算机上的进程发送数据,我们通常会用代码中的插座程序。
这时候,我们可选项一般也就TCP和UDP二选一。TCP可靠,UDP不可靠。除非是马总这种神级程序员(早期QQ大量使用UDP),否则,只要稍微对可靠性有些要求,普通人一般无脑选TCP就对了。
类似下面这样。
fd = socket(AF_INET,SOCK_STREAM,0);其中SOCK_STREAM,是指使用字节流传输数据,说白了就是TCP协议。
在定义了socket之后,我们就可以愉快的对这个socket进行操作,比如用bind()绑定IP端口,用connect()发起建连。
图片握手建立连接流程
在连接建立之后,我们可以使用 send()来发送数据 andrecv()来接收数据。
有了如此裸露的TCP连接,你就可以接收和发送数据,这还不够吗?
不,那会是个问题。
TCP具有三个特点:面向连接,可靠,和基于字节。
TCP是什么
这些三个特征的确很好地总结, 这些八段我们没有支持.
每个特征都可以在一个文章中讨论,今天我们需要集中讨论的是基于这个节点流。
字符流可以理解为数据通过双向通道流动,这些数据实际上我们经常称之为二进制数据,它只是一堆01个字符串。这些纯裸的TCP传输的01字符串之间没有界限,你不知道要从哪里得到完整的信息。
01二进制字节流
因为这没有边界特征,所以当我们选择使用TCP发送**"夏洛"和"特烦恼"的时候,接收器接收了夏洛特的麻烦,这时候接收端没发区分你是想要表达"夏洛"+"特烦恼"还是"夏洛特"+"烦恼"**。
消息对比
这就是所谓的粘贴问题,我曾经写过一篇特别的论文。
说这个的目的是为了告诉大家,纯裸TCP是不能直接拿来用的,你需要在这个基础上加入一些自定义的规则,用于区分消息边界。
因此,我们将所有我们想要发送的数据包起来,比如添加一个消息头条,详细说明一个完整的包的长度,根据这个长度数据可以继续接收,然后这些数据就是我们真正想要发送的实体。
消息边界长度标志
这里头条提到头条,你可以设置诸如消息体是否压缩和消息体的格式等等,只要所有事情都商定上下,并且每个人都认出对方,这就是所谓的协议。
每个使用TCP的项目可能定义一套类似的协议分析标准,它们可能不同,但原则是相似的。
许多协议, 例如HTTP和RPC, 都来自TCP.
让我们回顾一下网络的层次结构。
四层网络协议
TCP是传输层的协议,而基于TCP造出来的HTTP和各类RPC协议,它们都只是定义了不同消息格式的应用层协议而已。
HTTP协议(HyperTextTransferProtocol),又叫做超文本传输协议。我们用的比较多,平时上网在浏览器上敲个网址就能访问网页,这里用到的就是HTTP协议。
HTTP调用
RPC(RemoteProcedureCall)也称为远程过程调用(RemoteProcedureCall)。
例如,我们通常称之为这样一种地方方法。
res = localFunc(req)如果现在这不是个本地方法,而是个远端服务器暴露出来的一个方法remoteFunc,如果我们还能像调用本地方法那样去调用它,这样就可以屏蔽掉一些网络细节,用起来更方便,岂不美哉?
res = remoteFunc(req)
RPC可以像本地方法那样调用远程方法
基于这个想法,洋基开发了各种RPC协议,如著名的gRPC,thrift。
值得注意的是,虽然大部分RPC协议底层使用TCP,但实际上它们不一定非得使用TCP,改用UDP或者HTTP,其实也可以做到类似的功能。
HTTP和RPC基于TCP协议
现在我们回到文章的主题。
既然有HTTP协议, 为什么有RPC?
事实上,TCP是1970年代发行的协议,HTTP直到1990年代才流行。 直接使用裸体TCP有问题,你知道有多少自定义的协议已经存在多年,还有一个RPC从1980年代发行。
所以我们不是问为什么应该有一个RPC,因为有一个HTTP协议,而是问为什么应该有一个HTTP协议和RPC。
现在所有的网络软件都安装在计算机上,比如xx管家,xx卫士,它们都作为客户端(client)需要跟服务端(server)建立连接收发消息,当前正在使用应用程序层协议,在这个**客户端/服务器(c/s)**架构下,他们可以使用自己的RPC协议,因为它只管理自己的公司服务器。
但有个软件不同,浏览器,不管是Chrome还是IE,他们不仅需要能够访问自己的公司服务器,你还需要访问其他公司的网站服务器,所以他们需要统一的标准,不然大家没法交流。于是,HTTP就是那个时代用于统一browser/server (b/s)的协议。
就是,几年前,HTTP主要用于b/s架构,RPC更用于c/s架构。但现在情况并不那么清楚,b/s和c/s慢慢融合。许多软件同时支持多个目的,比如某度云盘,既要支持网页版,它还支持移动电话和PC终端,如果所有通信协议使用HTTP,这个服务器用同样的组件就足够了。RPC开始在幕后留下,一般用于公司内部集团,不同微型服务之间的通信。
那都是HTTP, RPC是什么?
好像我们回到文章的开头了,那从它们之间的差异开始。
让我们看看RPC和HTTP之间的一些明显的区别。
为启动向服务器请求,您必须首先建立连接,建立连接的前提是您知道IP地址和端口。 为该服务找到相应的IP端口的过程实际上是服务发现。
在HTTP中,您知道该服务的域名,并可以通过DNS服务分析其背后的IP地址,默认为80个端口。
而RPC的话,就有些区别,通常有一个专门的中间服务存储服务名称和IP信息,例如,领事等,甚至是redis。想要访问某个服务,到这些中间服务获取IP和港口信息。因为dns也是一个被发现的服务,所以还有基于dns的组件来进行服务发现,例如, CoreDNS。
可以看出,服务发现这幅作品,两者略有不同,但没有太多可以分开。
以主流的HTTP1.1协议为例,其默认在建立底层TCP连接之后会一直保持这个连接(keep alive),之后的请求和响应都会复用这条连接。
而RPC协议,也跟HTTP类似,通过建立与数据交互的TCP长链路,但不同的地方在于,RPC协议通常重建一个连接池,在请求量大的时候,在池中建立多个连接,当你想发送数据时,你从池中取出一个连接,用完放回去,下次再复用,可以说非常环保。
connection_pool
由于连接池有利于提升网络请求性能,所以不少编程语言的网络库里都会给HTTP加个连接池,比如go就是这么干的。
可以看出,这幅作品并没有太大的区别,所以这不是关键。
基于TCP传输的消息,说到底,无非都是消息头header和消息体body。
头条用于标记某些特殊信息, 最重要的是消息的长度.
身体是我们真正需要传递的内容,这些内容只能是二进制01字符串,毕竟, 电脑只知道这个游戏.所以TCP传输字符串和数字并不是问题,因为字符串可以转换为编码和转换为01字符串,数字本身也可以直接转换为二进制。但结构体呢,我们还得找出一种把它转换成二进制01字符串的方法。许多这样的计划已经实施,比如json,protobuf。
将结构转换成二进制序列的过程称为序列化,而将二进制序列重构成结构的过程则称为逆序列化。
序列化和反序列化
对于主流HTTP1.1,虽然现在它被称为超文本协议,支持音频视频,但是HTTP的设计最初是用来显示网页文本的,因此它传输的内容主要是一个字符串。头条和身体是一样的。在body这块,它使用json来序列结构数据。
我们可以拍一张照片,看看它。
HTTP报文
你可以看到,这里面有很多冗余,显得非常啰嗦。最明显的,如标题中的信息,事实上,如果我们同意第一个数字是内容类型,就不需要每次都真的把"content-type"这个字段都传过来,类似的情况实际上在身体的json结构中尤为明显。
而RPC,由于高度的定制,它可以用更小的protobuf或其他序列协议存储结构数据。也不需要考虑HTTP等浏览器的行为,例如, 302是跳转的跳跃吗?结果会更好,它还放弃HTTP在公司内部微型服务中,选择使用RPC的主要原因。
HTTP原理
RPC原理
当然,上面提到的HTTP,事实上,具体指的是当前主流的HTTP1.1使用,HTTP2基于前者做了大量的改进,因此性能可能比许多RPC协议更好,甚至使用HTTP2直接在gRPC底部。
那么问题又来了。
因为HTTP2发布于2015年,当时许多公司内部的RPC协议已经运行多年,而且由于历史原因,它们一般不需要被更换。
本文由 在线网速测试 整理编辑,转载请注明出处。