Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

学习RPC #61

Closed
xiaoliuzi opened this issue Jan 31, 2016 · 3 comments
Closed

学习RPC #61

xiaoliuzi opened this issue Jan 31, 2016 · 3 comments
Labels

Comments

@xiaoliuzi
Copy link
Owner

No description provided.

@xiaoliuzi
Copy link
Owner Author

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息的到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

要熟悉RPC请先思考这样两个问题:
你编过程序吧?你程序里有函数或方法间的调用吧?

比如你写了两个函数fa和fb,在fa里肯定可以调用fb,这个可以理解吧?好了,铺垫完了。接下来入正题: 别人写了个程序,程序里有个函数rf,而且这个程序是独立运行的。你自己写程序时想调用这个rf,怎么办?rpc就是为了解决这个问题而出现的技术。远程过程调用就是一种在两个原本相互独立的进程间建立一种类似于单个程序内函数间调用的函数调用机制。这样,不仅一个程序内的函数可以相互调用,不同程序间的函数也可以相互调用了。 至于用途,你可以从程序内函数调用的用途出发进行思考。首先是可以直接利用别的程序的部分功能,这是最基础的。更重要的,利用rpc可以实现系统的分布式架构,一方面有些功能比较相关应该放到一起实现,一方面物理因素的原因要求系统分解为多机实现,因此有的功能实现为了一个机器上的进程,而另外的功能实现为在另外机器上的进程,这两个进程间的协同和信息交互就可以通过rpc来实现。 另外,有些现成系统所设计的扩展方式就是要通过rpc实现,这时你就需要通过它所选择的rpc协议编程方式对原系统进行功能扩展。

为了便于理解这里再举个例子。你操作你自己电脑硬盘上的文件,是个方法,和访问网络上的文件,在使用上没什么区别,但实际实现上存在差异。大体的差异你肯定能理解。至少有各种网络协议吧。rpc就是类似的东西。说来说去,就是不同计算主体之间数据协同问题。无非传统编写程序,函数和函数之间的调用,数据的协同很容易,基本在你的代码设计时,就考虑到了数据空间的设计和调用,甚至没有这个概念,只有局部变量和全局变量的概念。而跨进程的相互协同运行,则就多了数据协同的问题。有同步也有异步,不过总有同步点。

@xiaoliuzi
Copy link
Owner Author

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:刘鹏
链接:https://www.zhihu.com/question/25536695/answer/54052580
来源:知乎

RPC, 远程过程调用直观说法就是A通过网络调用B的过程方法。

通信中的协议是你自己规定的,比如你可以规定说当A向B发送数字1, B就打印hello world, 并返回数字1给A, 如果发送数字2,B就打印hello, guy.并发送数字2给A.

这就是一个简单的RPC示例,实际上natty就是干这个的,只不过它提供一套框架给你,让你可以定义自己的规则,实现B里面的函数。

我们来想一想,当A要把数字1发送给B要怎么办呢,你需要用socket,B是server, A是client, B执行完以后再把1通过socket写回即可。你可以用AUPE书中的例子很容易就构建出来。

问题到这里,应该就结束了,事实上就是这么简单,那为什么需要RPC框架呢?当你给B发送1或者2就可以区分函数方法,但是你不可能写100个函数打印100个字符,你需要在发送1的时候,再接着发送一个字符串,这样B只要实现一个通用的print函数就够了。

我们继续思考,当你想发送一个数字比如65534给B的时候,我们知道socket是按照字节接受数据的,一个字节最大为255也就是0xff, 如果要发送65534你有两种方法,第一把65534当作5个字符6,5,5,3,4然后让socket接受5个字符。

第二,你可以把65534变成二进制0xfffe, 这样你可以先发送一个0xff再发送一个0xfe,也就是先发一个255再发一个254再拼装。

另外在拼装这255和254的时候,我们知道不同体系结构的CPU字节序是不一样的,因此,你需要解决大端小端字节序的问题。否则对于一个32位机器上,65534作为一个int型可能会是0xfffe0000,或者0x0000feff,这取决于你如何发送和打包它。

对于相对复杂的RPC, 我们发送的一个数据包往往既包含字符串又包含数字,这样我们就需要把他们切割开来然后分别解码编码。

这个部分就叫做序列化反序列化,在高级一点的RPC框架中,甚至可以做到把一个类从A扔给B.用到的还是这个方法,只是把类这个类型也放进去了。

对于脚本语言比如Lua,你甚至可以把编译过后的字节码发给远程,然后在B用Lua虚拟机执行。

所以对于一个完整的RPC框架底层往往是socket搭配序列化反序列化的工作。

问题到这里,一个RPC分析应该也结束了, 实际上要想把一个RPC做稳定了,还有几个问题。

首先,要想让一个RPC能够更高效,往往做到让A可以连续向B发送请求,这就要求在协议解析的时候区分两个包,在TCP分包的时候这里有个问题叫做半包和粘包。

另外B也无需在返回完函数1以后再返回函数2,这就要求在返回的时候,A能够区分开来,这就要求在协议里面加入session.高效的写法不但在编解码和协议设计理念,还需要在编写socket server时候,尽可能降低阻塞调用,用异步来做,这有一大推开源方案,比如libev.netty就是一个这样异步的RPC通信框架。

当然,在实际项目中,我们只是需要解决实际问题,完全没必要从头构建,你可以选一个通信协议比如protobuf/JSON/msgpack/thrift等等再找他相关的RPC库使用即可。

当然完全可以自己定制。一个好的框架不只是解决最基本的问题,它更像是一个生态系统,比如我看到netty还有SSL的部分,DNS部分。Have Fun.

@xiaoliuzi
Copy link
Owner Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant