1 Star 0 Fork 10

vbird/rpc-backend-cpp

forked from Plato/rpc-backend-cpp 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

RPC C++ 框架

基于RPC Frontend产生的配置生成RPC框架,框架本身不包含socket通信相关代码,但可以快速与已有的网络通信框架集成。

RPC调用流程

使用

包含rpc.h,在框架主循环内调用rpc::update(), 并实现Transport接口,Transport接口作为RPC协议的输入流,当有RPC协议到来的时候调用rpc::onMessage()方法。

Transport接口如下:

class Transport {
public:
    virtual int send(const char* data, int size) = 0;
    virtual int peek(char* data, int size) = 0;
    virtual int recv(char* data, int size) = 0;
    virtual int skip(int size) = 0;
    virtual int size() = 0;
    virtual bool isClose() = 0;
    virtual void close() = 0;
};

在逻辑主循环内调用:

#include "rpc.h"
...

rpc::update();

当有RPC协议到来时调用:

// transportPtr为用户实现的RPC数据流包装器智能指针实例
rpc::onMessage(tranportPtr);

IDL定义

请参考RPC Frontend

生成RPC代码

代码生成流程如下:

  1. 使用RPC Frontend将IDL转换为两个JSON文件,譬如test1.idl使用将转换为:test1.idl.cpp.json和test1.idl.protobuf.json
  2. 运行如下脚本:
python cppgen.py test1.idl.cpp.json
python cppgen_pb_layer.py test1.idl.cpp.json test1.idl.protobuf.json

文件命名规则

由框架生成的代码文件命名规则如下:

  1. 所有文件名由IDL文件名作为起始
  2. 单个IDL文件所有引用到的所有struct被生成在单独的文件内

由下文所述的test1.idl,其中文件名为test1, 服务名为Service,生成的代码文件包含:

文件名 描述
test1.struct.h 所有struct定义
test1.service.Service.h 用户接口定义
test1.service.Service.proxy.h(.cpp) 代理头文件(实现文件)
test1.service.Service.stub.h(.cpp) 服务桩头文件(实现文件)
test1.service.Service.proxy.serializer.h 代理序列化相关头文件
test1.service.Service.stub.serializer.h 服务桩序列化相关头文件

以下文件为protobuf序列化层,主要由.proto文件及protobuf的C++头文件和实现文件,protobuf序列化实现,这样实现的原因是形成序列化上层和下层,上层可以保持代码不更改的情况下使用不同的序列化方案实现。

文件名 描述
test1.service.proto protobuf定义文件
test1.service.pb.h protobuf生成的头文件
test1.service.pb.cc protobuf生成的实现文件
test1.service.Service.proxy.serializer.cpp 使用protobuf实现的代理序列化下层
test1.service.Service.stub.serializer.cpp 使用protobuf实现的桩序列化下层

调用方编译所需文件为:

文件名 描述
test1.struct.h 所有struct定义
test1.service.Service.proxy.h(.cpp) 代理头文件(实现文件)
test1.service.Service.proxy.serializer.h 序列化相关头文件
test1.service.Service.proxy.serializer.cpp 使用protobuf实现的序列化下层
test1.service.pb.h protobuf生成的头文件
test1.service.pb.cc protobuf生成的实现文件

服务提供方编译所需文件为:

文件名 描述
test1.struct.h 所有struct定义
test1.service.Service.h 用户接口定义
test1.service.Service.stub.h(.cpp) 服务桩头文件(实现文件)
test1.service.Service.stub.serializer.h 序列化相关头文件
test1.service.Service.stub.serializer.cpp 使用protobuf实现的序列化下层
test1.service.pb.h protobuf生成的头文件
test1.service.pb.cc protobuf生成的实现文件

类命名规则

如下文的test1.idl定义内的服务Service,生成的代理类名为ServiceProxy,生成的用户需实现的接口为ServiceImpl,这个类是用户实现的服务功能

使用

假设有如下IDL文件, test1.idl

struct Dummy {
    i32 field1
    string field2
    seq<string> field3
    set<string> field4
    dict<i64,string> field5
    bool field6
    float field7
    double field8
}

struct Data {
    i32 field1
    string field2
    seq<string> field3
    set<string> field4
    dict<i64,string> field5
    bool field6
    float field7
    double field8    
    Dummy field9
    seq<Dummy> field10
    set<string> field11
    dict<i64,Dummy> field12
}

service Service multiple=16 {
    oneway void method1(Data,string)
    string method2(i8,set<string>,ui64)
    dict<i64,Dummy> method3(i8,set<string>,dict<i64,string>, ui64)
    void method4(void)
    void method5()
}


通信代码生成后,可以按如下方法使用:

获取服务

服务发现和建立网络连接由使用者负责建立。

#include "test1.service.Service.proxy.h"

// trans为用户提供的Transport
auto service = rpc::createProxy<ServiceProxy>(trans);
  1. 异步调用方式
service->method2(1, {"2", "3"}, 4, [&](const std::string& ret) {
    ......
}
  1. 协程调用方式
co (
    auto ret = service->method2(1, {"2", "3"}, 4);
    ......
)

错误处理

  1. 获取服务
    getService当服务找不到时会返回空
  2. 在协程内调用代理服务时远端服务异常
    当远端服务在调用中发生异常则代理服务也将抛出异常,异常类型为rpc::RemoteMethodException
  3. 在协程内调用代理服务时,由于版本不匹配,方法未找到
    通过代理服务调用将抛出异常,异常类型为rpc::MethodNotFound
  4. 在协程内调用代理服务时调用超时
    通过代理服务调用将抛出异常,异常类型为rpc::RemoteMethodTimeout
  5. 在co(...)外调用协程方法
    在co(...)外调用协程方法将抛出异常,异常类型为rpc::ProxyCoroMethodException

编写服务

通过脚本生成代码后就需要开发实际的服务功能了,用户除了需要实现自己定义的服务方法外,还需要实现如下几个被框架调用的方法:

virtual bool onAfterFork() override;
virtual bool onBeforeDestory() override;

如本文中的例子,ServiceImpl的构造函数不允许有参数,个性化构造通过实现onAfterFork方法来实现,当服务实例被销毁前框架会调用onBeforeDestory,在这个方法可以做清理及数据保存工作。 服务在编译和使用方式上分为两类:

  1. 静态服务
    静态服务即服务的代码被编译到调用程序中,可以以源代码的方式或者静态库的方式
  2. 动态服务
    动态服务是被编译成共享对象的方式(.so, .dll),通过运行时动态加载的方式使用

同一个服务的静态形式和动态形式不能同时存在,只能二选其一. 动态服务有几个额外的特性:

  1. 动态服务可以被热更新
    动态服务可以在运行时更新,假设有一个动态服务test1.0.so, 这个服务的热更新包是test1.1.so,可以在不卸载test1.0.so的情况下加载test1.1.so, 加载后所有原有由test1.0.so产生的服务将缓存新到来的调用请求,当这些服务执行完正在调用的请求时会尝试卸载test1.0.so,直到所有引用test1.0.so的服务都不再使用时test1.0.so会被卸载,如果加载test1.1.so后因为某个原因启动失败,则升级过程终止,继续使用test1.0.so来进行服务
  2. 因为动态服务是被独立编译的,可以把那些代码相对独立的功能模块隔离开发,设计者在设计时会更多的考虑如何减少与其他系统的耦合

通信协议

为了与库的RPC兼容,其他系统或语言实现接入时需要实现既定的通信协议格式,协议格式如下:

协议头 协议体

协议头

RpcMsgHeader

协议长度(包含消息头) 协议类型 协议体
32位 32位

协议类型如下:
0 无效类型
1 调用请求
2 调用返回
3 非RPC协议

调用请求

RpcCallHeader

消息头(RpcMsgHeader) 服务UUID 绑定的服务器实例ID 代理调用ID 方法ID 协议体
64位 64位 32位 32位 32位

调用返回

RpcCallRetHeader

消息头(RpcMsgHeader) 绑定的服务器实例ID 代理调用ID 错误码 协议体
64位 32位 32位 32位

代理调用请求

RpcProxyCallRequestHeader

消息头(RpcMsgHeader) 服务UUID 绑定的服务器实例ID 代理调用ID 外部连接标识ID 是否是oneway调用 协议体
64位 64位 32位 32位 32位 16位

代理调用返回

RpcProxyCallRetHeader

消息头(RpcMsgHeader) 绑定的服务器实例ID 代理调用ID 错误码 外部连接标识ID 协议体
64位 32位 32位 32位 32位

错误码如下:
1 成功
2 服务或方法未发现
3 服务发生异常
4 调用超时

MIT License Copyright (c) 2019 dennis-kk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

C++ RPC框架 展开 收起
README
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/vbirds/rpc-backend-cpp.git
git@gitee.com:vbirds/rpc-backend-cpp.git
vbirds
rpc-backend-cpp
rpc-backend-cpp
v0.3.0-alpha

搜索帮助