本文来自srpc作者李颖欣,在此基础上略做改动。
只要涉及到网路通讯,必然涉及到网路合同,应用层也是一样。在应用层最标准和常用的就是HTTP合同。但在好多性能要求较高的场景各大企业内部也会自定义的RPC合同。举个事例,就是相当于各个省不但用官方普通话,还都有自己的土语,RPC就相当于是一个土语。
RPC的全称是RemoteProcedureCall,翻译过来就是远程过程调用。但这个名子起的一点都不好,过于指出了和LPC(本地过程调用)的对比。没有突出下来RPC本身涉及到的一些技术特征。
我们昨天来从三个角度和你们说说RPC。
明天的讲解会结合基于C++实现的开源项目SRPC。SRPC整体代码风格简约、架构层次精致,整体约1万行代码,特别适宜拿来学习RPC构架:
一.RPC是哪些
RPC可以分为两部份:用户调用插口+具体网路合同。后者为开发者须要关心的,前者由框架来实现。
1.用户调用插口
举个反例,我们定义一个函数,我们希望函数倘若输入为“HelloWorld”的话,输出给一个“OK”,这么这个函数是个本地调用。假如一个远程服务收到“HelloWorld”可以给我们返回一个“OK”,这么这是一个远程调用。我们会和服务约定好远程调用的函数名。为此,我们的用户插口就是:输入、输出、远程函数名,例如用SRPC开发的话,client端的代码会长这样:
int main()
{
Example::SRPCClient client(IP, PORT);
EchoRequest req; // 用户自定义的请求结构
EchoResponse resp; // 用户自定义的回复结构
req.set_message("Hello World");
client.Echo(&req, &resp, NULL); // 调用远程函数名为Echo
return 0;
}
2.具体网路合同
这是框架来实现的,把开发者要发出和接收的内容以某种应用层合同打包进行网路收发。这儿可以和HTTP进行一个显著的对比:
3.进一步思索
上图对应的颜色,所实现的功能是类似的。我们想一想,为何你们都长差不多呢?
这儿就须要厘清楚,我们想要实现用户插口,须要如何做?最重要须要支持以下三个功能:
这样既可以让消息内保证一定的灵活性linux内核 tcp协议栈,又可以便捷拿下一块数据,去调用用户想要的服务。
我们用一个表格来看一下HTTP和RPC分别是如何解决的:
定位要调用的服务消息宽度消息前后兼容HTTPURLheader里Content-Lengthbody里自己解决RPC指定Service和Method名合同header里自行约定交给具体IDL
为此,你们就会须要类似的结构去组装一条完整的用户恳求,而第三部份的body只要框架支持,RPC合同和HTTP是可以互通的!为此开发者完全可以按照自己的业务需求进行选型,接出来我们看一下RPC的层次构架,就可以明白为何不同RPC框架之间的互通、以及RPC和HTTP合同又是怎么做到互通的。
二、RPC有哪些
我们可以借SRPC的构架,看一下RPC框架从用户到系统都有什么层次,以及SRPC目前所纵向支持的功能是哪些:
我们先关注以下三个层级:
如图从左到右,是用户接触的最多到最少的层次。IDL层会依据开发者定义的恳求/回复结构进行代码生成,目前男子伴们用得比较多的是protobuf和thrift,而刚刚说到的用户插口和前后兼容问题,都是IDL层来解决的。SRPC对于这两个IDL的用户插口实现方法是:
中间那列是具体的网路合同,而各RPC能互通,就是由于你们实现了对方的“语言”,因而可以合同互通。
而RPC作为和HTTP并列的层次,第二列和第三列理论上是可以两两结合的,只须要第二列的具体RPC合同在发送时,把HTTP相关的内容进行特化,不要根据自己的合同去发,而根据HTTP须要的方式去发,就可以实现RPC与HTTP互通。
相关视频推荐
90分钟看懂分布式RPC开源框架-gRPC
看完《tcp/ip解读》不能coding的,一次课开启设计tcp/ip合同栈
学习地址:C/C++Linux服务器开发/后台构架师【零声教育】-学习视频教程-腾讯课堂
须要C/C++Linux服务器构架师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,解释器,DPDK,ffmpeg等),免费分享
三、RPC的生命周期
到此我们可以通过SRPC看一下,把request通过method发送出去并处理response再回去的整件事情是如何做的:
按照上图,可以更清楚地看见刚刚提到的各个层级linux内核 tcp协议栈,其中压缩层、序列化层、协议层虽然是相互前馈打通的,在SRPC代码上实现得十分统一,纵向降低任何一种压缩算法或IDL或合同都不须要也不应当改动现有的代码,才是一个精致的构架~
我们仍然在说生成代码linux服务器配置与管理,究竟有哪些用呢?图中可以获知,生成代码是衔接用户调用插口和框架代码的桥梁,这儿以一个最简单的protobuf自定义合同为例:example.proto
syntax = "proto3"; // 这里proto2和proto3都可以
message EchoRequest
{
string message = 1;
};
message EchoResponse
{
string message = 1;
};
service Example
{
rpc Echo(EchoRequest) returns (EchoResponse);
};
我们定义好了恳求、回复、远程服务的函数名,通过以下命令就可以生成出插口代码example.srpc.h:
protoc example.proto --cpp_out=./ --proto_path=./
srpc_generator protobuf ./example.proto ./
我们会发觉,同时就会生成出和,这是为了便捷开发者的两个空文件。我们继续一窥到底,瞧瞧生成代码究竟可以实现哪些功能:
// SERVER代码
class Service : public srpc::RPCService
{
public:
// 用户需要自行派生实现这个函数,与刚才pb生成的是对应的
virtual void Echo(EchoRequest *request, EchoResponse *response,
srpc::RPCContext *ctx) = 0;
};
// CLIENT代码
using EchoDone = std::function;
class SRPCClient : public srpc::SRPCClient
{
public:
// 异步接口
void Echo(const EchoRequest *req, EchoDone done);
// 同步接口
void Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx);
// 半同步接口
WFFuture<std::pair> async_Echo(const EchoRequest *req);
};
作为一个高性能RPC框架,SRPC生成的client代码中包括了:同步、半同步、异步插口,文章开头展示的是一个同步插口的做法。
而server的插口就更简单了,作为一个服务端,我们要做的就是收到恳求->处理逻辑->返回回复,而这个时侯,框架早已把刚刚提及的网路收发、解压缩、反序列化等都给做好了,之后通过生成代码调用到用户实现的派生service类的函数逻辑中。
因为一种合同定义了一种client/server,因而虽然我们同样可以得到的server类型有第二部份提及过的若干种:SRPCServer/SRPCHttpServer/BRPCServer/TRPCServer/ThriftServer/...
四、一个完整的server事例
最后我们用一个完整的server事例,来看一下用户调用插口的使用方法,以及怎样跨合同使用HTTP作为client进行调用。刚刚提及,srpc_generator在生成插口的同时linux内存管理,也会手动生成空的用户代码,我们这儿打开直接改两行,即可run上去:
#include "example.srpc.h"
#include "workflow/WFFacilities.h"
using namespace srpc;
static WFFacilities::WaitGroup wait_group(1);
void sig_handler(int signo)
{
wait_group.done();
}
class ExampleServiceImpl : public Example::Service
{
public:
void Echo(EchoRequest *request, EchoResponse *response, srpc::RPCContext *ctx) override
{
response->set_message("OK"); // 具体逻辑在这里添加,我们简单地回复一个OK
}
};
int main()
{
unsigned short port = 80; // 因为要启动Http服务
SRPCHttpServer server; // 我们需要构造一个SRPCHttpServer
ExampleServiceImpl example_impl;
server.add_service(&example_impl);
server.start(port);
wait_group.wait();
server.stop();
return 0;
}
只要安装了srpc和workflow,linux下即可通过以下命令编译出可执行文件:
g++ -o server server.pb_skeleton.cc example.pb.cc -std=c++11 -lsrpc
接出来是兴奋人心的时刻了,我们用人手一个的curl来发起一个HTTP恳求:
curl -i 127.0.0.1:80/Example/Echo -H 'Content-Type: application/json' -d '{message:"Hello World"}'
五、解锁更多
通过这篇文章,相信我们可以清晰地了解到RPC的插口长哪些样,也可以通过与HTTP合同互通来理解合同层次,更重要的是可以晓得具体横向的每位层次及纵向对比我们常见的每种使用模式都有什么。但显然,RPC还可以做的事情还有好多,包括内部各层次的前馈合设计、框架层的功能埋点、外部服务集群的对接等等: