RPC原理与FastRPC实现
Posted TensorBoy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RPC原理与FastRPC实现相关的知识,希望对你有一定的参考价值。
0. 起因
最近看文档,发现一些组件是通过FastRPC来进行沟通的,并且偶尔看到某些场景下在FastRPC上的时间消耗好像也蛮可观,恰好FastRPC是开源的,因此决定看看FastRPC具体的实现。
1. RPC简介
Fig 1 RPC internal
情况就是这么个情况,但是由于RPC只是一种实现方法,并没有形成标准,因此有着很多不同的实现,比如gRPC、Dubbo、Thrift和FastRPC等。对于如何进行序列化和反序列化、如何通信,不同的框架有着不同的实现。
2. FastRPC简介
FastRPC是一个XML-RPC协议的实现,它的特点是有多种数据序列化方式可选:二进制、JSON、XML以及Base64,因为它使用HTTP协议作为载体,通过HTTP的头的数据格式协商字段很容易知道数据的格式。
FastRPC相对与其他框架来说非常简单,代码主要就三部分:客户端实现、服务端实现以及数据序列化与反序列化的实现。也是因为它太简单了,它并没有涉及到鉴权等方面的内容,这些需要自己去考虑。但是对于了解一个RPC框架的具体实现已经足够了。
3. FastRPC调用流程
3.1. FastRPC Server调用流程
FastRPC Server的实现如图2所示,其工作流程如下:
例程注册完成后,开始启动对特定的端口的监听;
当有请求到达后,通过HTTP的头确定内容的序列化格式,调用对应的反序列化方法对数据进行解析。
函数返回后通过将数据返回请求者。
整个过程非常简单。
Fig 2 FastRPC Server(点击查看原文获取高清图片)
3.2. FastRPC Client的调用流程
FastRPC Client的实现如图3所示,其工作流程如下:
配置完成后等待客户调用,客户调用特定方法,传入了参数。客户端调用选定的序列化例程对数据进行序列化,并且根据数据填充HTTP头信息;
客户端发起HTTP请求,将数据发送个服务器,等待服务器应答;
获取到服务器返回的数据后,对数据进行解析,返回给调用者。
Fig 3 FastRPC Client(点击查看原文获取高清图片)
整个请求的核心就是下面的几行代码,位于frpcserverproxy.cc
中,首先将方法名和参数都序列化到本地的缓存,然后通过flush
方法写到HTTP输出流:
// fastrpcsrcfrpcserverproxy.cc
...
try {
marshaller->packMethodCall(methodName);
// marshall all passed values until null pointer
while (const Value_t *value = va_arg(args, Value_t*))
feeder.feedValue(*value);
marshaller->flush();
} catch (const ResponseError_t &e) {}
...
// fastrpcsrcfrpctreefeeder.cc
template <typename Marshaller_t>
void feedValueImpl(Marshaller_t &marshaller, const Value_t &value) {
switch(value.getType()) {
case Int_t::TYPE:
marshaller.packInt(Int(value).getValue());
break;
case Bool_t::TYPE:
marshaller.packBool(Bool(value).getValue());
break;
case Null_t::TYPE:
packNull(marshaller, value);
break;
...
// fastrpcsrcfrpcbinmarshaller.cc
void BinMarshaller_t::packInt(Int_t::value_type value) {
if (protocolVersion.versionMajor > 2) {
// pack via zigzag encoding.
uint64_t zig = zigzagEncode(value);
unsigned int numType = getNumberType(
static_cast<Int_t::value_type>(zig));
//pack type
char type = FRPC_DATA_TYPE(INT, numType);
//pack number value
Number_t number(zig);
//write type
writer.write(&type, 1);
writer.write(number.data, getNumberSize(numType));
} else if (protocolVersion.versionMajor > 1) {
...
// fastrpcsrcfrpchttpclient.cc
void HTTPClient_t::write(const char* data, unsigned int size) {
contentLenght += size;
if (size > (BUFFER_SIZE - queryStorage.back().size())) {
if (useChunks) {
sendRequest();
queryStorage.back().append(data, size);
} else {
if (size > BUFFER_SIZE) {
queryStorage.push_back(std::string(data, size));
} else {
queryStorage.back().append(data, size);
}
}
} else {
queryStorage.back().append(data, size);
}
}
// fastrpcsrcfrpchttpclient.cc
void HTTPClient_t::sendRequest(bool last) {
SocketCloser_t closer(httpIO.socket());
std::string headerData;
if (!headersSent) {
HTTPHeader_t header;
StreamHolder_t os;
//create header
os.os << POST << ' ' << (url.isUnix() ? "/" : url.path) << ' '
<< (useHTTP10 ? HTTP10 : HTTP11) << " ";
if (!useHTTP10) {
os.os << HOST << ": ";
if (!url.isUnix()) {
os.os << url.host << ':' << url.port;
}
os.os << " ";
}
...
4. 总结
说白了,RPC最终就是一个服务器和客户端。那么RPC和REST(Representational State Transfer,表示层状态转移)有什么区别呢?两者的区别就是在于对服务器请求的目的不同:REST需要服务器给出资源,例如一张图片一段数据,并且通过GET, PUT, UPDATE, DELETE
等特定方法表述希望对这个资源进行的操作;而RPC客户端需要RPC服务器提供计算服务,你用我希望的任何方式对我提供的数据进行计算并告诉我结果。
长按上方二维码关注或点击左上角蓝字关注我,获取更多技术文章。
C++ | Python | Linux | 推理引擎 | AI框架源码
有一起玩耍的么?
5. References
[1] https://github.com/seznam/fastrpc
以上是关于RPC原理与FastRPC实现的主要内容,如果未能解决你的问题,请参考以下文章