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所示,其工作流程如下:

  1. 例程注册完成后,开始启动对特定的端口的监听;

  2. 当有请求到达后,通过HTTP的头确定内容的序列化格式,调用对应的反序列化方法对数据进行解析。

  3. 函数返回后通过将数据返回请求者。

整个过程非常简单。


RPC原理与FastRPC实现

Fig 2 FastRPC Server(点击查看原文获取高清图片)


3.2. FastRPC Client的调用流程

FastRPC Client的实现如图3所示,其工作流程如下:

  1. 配置完成后等待客户调用,客户调用特定方法,传入了参数。客户端调用选定的序列化例程对数据进行序列化,并且根据数据填充HTTP头信息;

  2. 客户端发起HTTP请求,将数据发送个服务器,等待服务器应答;

  3. 获取到服务器返回的数据后,对数据进行解析,返回给调用者。

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实现的主要内容,如果未能解决你的问题,请参考以下文章

面向服务架构之RPC原理与实例

一文详解RPC框架核心原理与手写实战

RPC的实现与远程调用的实现机制

RPC的实现与远程调用的实现机制

微服务学习RPC原理与Go RPC

go微服务RPC的原理与Go RPC