目录
[三.RpcCaller 模块设计思想](#三.RpcCaller 模块设计思想)
[4.2. 异步调用函数](#4.2. 异步调用函数)
[4.3. 回调式调用函数](#4.3. 回调式调用函数)
一.Requestor模块设计思想
Requestor模块存在的意义:针对客户端的每一条请求进行管理,以便于对请求对应的响应作出合适的操作。
Requestor模块在客户端网络通信架构中扮演着核心管理者的角色。
其根本意义在于建立一套严谨的请求与响应匹配机制,以应对高并发、异步网络环境下的核心挑战:确保每一条响应都能被安全、准确地递送给对应的请求发起方,从而保障业务逻辑的正确性。
以下是对其设计背景、核心原理及高级功能的详细阐述:
一、 应对的核心挑战
-
多线程并发下的时序错乱
在一个多线程的客户端程序中,多个线程可能同时向服务器发起请求 。由于网络延迟、服务器处理速度差异、以及可能的负载均衡等因素,服务器返回响应的顺序与客户端发送请求的顺序极有可能不一致。 如果没有一个有效的匹配机制,**客户端线程在收到一个响应时,将无法判断这个响应是属于本线程的请求,还是属于其他线程的请求。**这种不确定性将直接导致数据混乱、状态错误,是系统稳定性的严重威胁。
-
异步I/O模型的非阻塞特性
在现代高性能网络库(如Muduo、Netty)的异步I/O模型中,I/O操作是事件驱动和非阻塞的。
-
发送:调用发送接口仅是将数据放入操作系统的发送缓冲区或库的内部队列,真正的网络传输由系统在后台异步完成。调用后函数立即返回,不代表数据已送达对端。
-
接收 :没有传统的、主动调用的
recv或read函数来等待数据。相反,当套接字变得可读时,网络库会自动读取数据,并调用预先注册的全局消息到达回调函数。 -
这种模型带来了高性能,但也彻底改变了编程范式。客户端在"发出请求"的动作和"收到响应"的事件之间是断开的,无法通过简单的同步等待来获取特定请求的结果。
-
二、 核心解决方案:请求ID与中央注册表
Requestor模块的核心思想是引入一个请求唯一标识符(Request ID) 和一块中央共享状态,以解耦请求的发送与响应的处理。
-
请求标识化:客户端在构造每一条待发送的请求时,为其分配一个全局唯一的ID(如递增序号、UUID等)。这个ID作为请求的一部分被发送至服务器。
-
响应关联 :协议约定,服务器在处理完请求后,必须在返回的响应数据包中原样携带该请求的ID。
-
建立中央注册表 :客户端内部维护一个并发安全的数据结构(通常是一个哈希表) ,作为请求-响应的中央注册表。该表以 请求ID为键。
-
请求注册与响应派发:
-
当发送一条请求 时,模块会先在中央注册表中以该请求的ID为键,创建一个**"槽位"** 或"承诺"(Promise)。这个**"槽位"**用于暂存未来到达的响应数据。
-
随后,请求被异步发送出去。
-
当服务器返回一个响应的时候,我们的Muduo网络库的全局消息到达回调函数onMessage被触发时,它从数据包中解析出响应和其中包含的请求ID。
-
消息到达回调函数onMessage并不关心这个响应具体对应哪个业务逻辑,它只执行一个标准操作:根据 请求ID,找到中央注册表中对应的那个"槽位" ,并将响应数据存入其中。
-
-
提供可控的获取接口:模块对外提供getResponse(requestId)这样的接口。对于发起请求的线程或业务逻辑而言:
-
它可以阻塞等待,直到指定ID的响应数据被填入槽位后返回。这模拟了同步调用,但底层仍是异步I/O。
-
关键在于,无论网络响应多么混乱,因为有了唯一的ID和中央注册表的精准映射,每个请求者都能准确无误地取回自己的响应,彻底解决了时序错乱问题。
-
三、 高级扩展:异步编程范式的支持
在基础模型之上,Requestor模块可以进一步封装,提供更现代化、更灵活的异步处理能力:
-
Future/Promise模式:
-
在创建请求槽位时,直接返回一个与这个槽位关联的std::future或类似的对象。
-
业务逻辑可以在任何需要的时候,通过这个future来尝试获取(get)或等待(wait_for)结果。
-
这使得"获取响应"这一操作本身也变成了一个可调度、可组合的异步任务,可以方便地集成到更复杂的异步链或并发流程中。
-
-
回调函数(Callback)机制:
-
在发送请求时,允许调用者注册一个专属的响应到达处理回调函数。
-
当响应到达并被存入中央注册表后,模块可以自动调度执行该回调函数,将响应结果作为参数传入。
-
这实现了真正的"事件驱动"编程风格,业务逻辑在请求发出后即可继续执行,结果将通过回调异步通知,非常适合UI事件处理或高吞吐量的服务器场景。
-
二.Requestor模块实现

2.1.请求描述结构体
当发送一个请求后,服务器响应是异步返回的。我们需要记录:
-
这个请求是谁发的(请求消息)
-
用什么方式等待结果(同步/异步/回调)
-
结果应该交给谁(promise或回调函数)
没有这个结构,响应回来时就不知道这个结果属于哪个请求,也不知道该怎么处理。
那么我们就有必要去写一个请求描述结构体。
cpp
// 智能指针别名定义
using ptr = std::shared_ptr<Requestor>;
// 请求回调函数类型定义,参数为响应消息的智能指针
using RequestCallback = std::function<void(const BaseMessage::ptr&)>;
// 异步响应类型定义,表示未来会获得的响应消息
using AsyncResponse = std::future<BaseMessage::ptr>;//因为父类指针可以存储子类指针
// 请求描述结构体,用于跟踪每个请求的状态
struct RequestDescribe {
// 智能指针别名
using ptr = std::shared_ptr<RequestDescribe>;
// 请求消息
BaseMessage::ptr request;
// 请求类型(同步/异步/回调)
RType rtype;
// promise对象,用于异步获取响应
std::promise<BaseMessage::ptr> response;
// 回调函数,用于回调方式处理响应
RequestCallback callback;
};
其中RType是我们之前定义好的枚举类型
cpp
/**
* 请求类型枚举
* 用于标识RPC请求的调用方式
*/
enum class RType
{
REQ_ASYNC = 0, ///< 异步请求:发送请求后不等待响应,立即返回
REQ_CALLBACK ///< 回调请求:发送请求后异步接收响应,通过回调函数处理结果
};
嗯?有的人说,怎么少了一种?
事实上,同步请求其实是
- 同步请求本质上就是 发送请求 + 立即等待响应 的异步请求
我们这里为了方便,我们的同步请求就借助了异步请求的代码来实现只不过多了立即等待响应这一步。
2.1.请求描述结构体的管理
既然,我们定义了这么一种请求描述结构体,我们的客户端肯定不止一个请求,那么每一个请求都需要创建一个请求描述结构体,这样子的话,客户端就会有很多请求描述结构体,那么我们就需要将其管理起来。
事实上呢,在我们的Requestor模块里面,就定义了一个哈希表<请求ID,请求描述结构体>,这样子将我们的请求描述结构体管理起来了。
但是,我们还需要对这个请求描述结构体提供专门的增删查接口。
- 增:创建新的RequestDescribe对象,将其添加到<请求ID,请求描述结构体>里面
- 删:根据请求ID将对应的RequestDescribe对象从<请求ID,请求描述结构体>里面删除
- 查:根据请求ID将对应的RequestDescribe对象从<请求ID,请求描述结构体>里面取出来
newDescribe函数(私有)
创建请求描述结构的内部辅助函数。
负责:
-
加锁保护共享数据(线程安全)
-
创建新的RequestDescribe对象
-
根据请求类型配置相应属性
-
将请求ID和描述对象的映射关系保存到内部表中
-
供其他send函数调用,建立请求跟踪机制
getDescribe函数(私有)
根据请求ID查找请求描述结构的内部辅助函数。
负责:
-
加锁保护共享数据
-
在内部映射表中查找指定ID的请求描述
-
返回找到的描述对象或空指针
-
被onResponse函数调用,用于匹配响应和请求
delDescribe函数(私有)
删除请求描述记录的内部辅助函数。
负责:
-
加锁保护共享数据
-
从内部映射表中删除指定ID的请求描述
-
防止内存泄漏,在请求处理完成后清理资源
这个思想是不是很简单???
cpp
// 请求器类,负责管理JSON-RPC请求的发送和响应处理
class Requestor
{
public:
// 智能指针别名定义
using ptr = std::shared_ptr<Requestor>;
// 请求回调函数类型定义,参数为响应消息的智能指针
using RequestCallback = std::function<void(const BaseMessage::ptr &)>;
// 异步响应类型定义,表示未来会获得的响应消息
using AsyncResponse = std::future<BaseMessage::ptr>; // 因为父类指针可以存储子类指针
// 请求描述结构体,用于跟踪每个请求的状态
struct RequestDescribe
{
// 智能指针别名
using ptr = std::shared_ptr<RequestDescribe>;
// 请求消息
BaseMessage::ptr request;
// 请求类型(同步/异步/回调)
RType rtype;
// promise对象,用于异步获取响应
std::promise<BaseMessage::ptr> response;
// 回调函数,用于回调方式处理响应
RequestCallback callback;
};
private:
// 创建新的请求描述对象
// 参数req: 请求消息
// 参数rtype: 请求类型
// 参数cb: 回调函数(仅对回调请求有效)
// 返回值: 请求描述对象的智能指针
RequestDescribe::ptr newDescribe(const BaseMessage::ptr &req, RType rtype,
const RequestCallback &cb = RequestCallback())
{
// 加锁保护共享数据
std::unique_lock<std::mutex> lock(_mutex);
// 创建请求描述对象
RequestDescribe::ptr rd = std::make_shared<RequestDescribe>();
// 设置请求消息
rd->request = req;
// 设置请求类型
rd->rtype = rtype;
// 如果是回调请求且有回调函数,设置回调
if (rtype == RType::REQ_CALLBACK && cb)
{
rd->callback = cb;
}
// 将请求描述存入映射表,以请求ID为键
_request_desc.insert(std::make_pair(req->rid(), rd));
return rd;
}
// 根据请求ID获取请求描述对象
// 参数rid: 请求ID
// 返回值: 请求描述对象的智能指针,如果不存在则返回空指针
RequestDescribe::ptr getDescribe(const std::string &rid)
{
// 加锁保护共享数据
std::unique_lock<std::mutex> lock(_mutex);
// 在映射表中查找请求ID
auto it = _request_desc.find(rid);
// 如果未找到,返回空指针
if (it == _request_desc.end())
{
return RequestDescribe::ptr();
}
// 返回找到的请求描述对象
return it->second;
}
// 根据请求ID删除请求描述对象
// 参数rid: 请求ID
void delDescribe(const std::string &rid)
{
// 加锁保护共享数据
std::unique_lock<std::mutex> lock(_mutex);
// 从映射表中删除指定请求ID的请求描述结构
_request_desc.erase(rid);
}
private:
// 互斥锁,用于保护_request_desc映射表的线程安全
std::mutex _mutex;
// 请求描述映射表,键为请求ID,值为对应的请求描述对象
std::unordered_map<std::string, RequestDescribe::ptr> _request_desc;
};
2.3.Requestor具体实现过程
2.3.1.std::promise的回顾
首先,我们需要理解std::future和std::promise的基本概念:
-
std::future:这是一个模板类,它提供了一种访问异步操作结果的机制。我们可以通过它来获取一个异步任务(比如在另一个线程中运行的函数)的返回值。但是,这个返回值可能还没有准备好,所以std::future提供了等待(wait)或检查(通过valid()等方法)的功能,直到异步操作完成并设置了值。
-
std::promise:这也是一个模板类,它提供了一种存储值(或异常)的方式,这个值可以被同一个线程或其他线程中的std::future对象读取。换句话说,std::promise允许你手动设置一个值,然后与它关联的std::future就可以读取这个值。
现在,我们来看它们如何配合工作:
-
当我们创建一个std::promise对象时,它内部会有一个共享状态 (shared state),这个共享状态可以存储一个值(或异常)。
-
通过调用std::promise的get_future()成员函数,我们可以获得一个与这个promise对象关联的std::future对象。
-
然后,我们可以在某个线程中运行一个任务,这个任务会计算一个值,然后通过调用std::promise的set_value()方法将值存储到共享状态中。
-
同时,在另一个线程(或同一个线程)中,我们可以通过std::future的get()方法来获取这个值。如果共享状态中还没有值,那么get()方法会阻塞,直到值被设置。
这样,我们就实现了一个线程向另一个线程传递数据的效果。
注意:一个std::promise对象只能关联一个std::future对象,并且只能设置一次值。如果多次设置,会抛出异常。
详细的工作流程
让我们跟着代码的典型生命周期走一遍:
步骤 1:创建配对
cpp
std::promise<int> prom; // 创建一个 promise 对象,用于将来产生一个 int 类型的值。
std::future<int> fut = prom.get_future(); // 从 promise 获取与之关联的 future 对象。
此时,共享状态被创建,但里面是"空"的。future 还没有就绪。
步骤 2:生产者设置值(让 future "就绪")
在生产者线程(比如一个工作线程)中,你操作 promise:
cpp
// 生产者线程
void producer(std::promise<int>& prom) {
// ... 执行一些复杂的计算 ...
int result = 42;
prom.set_value(result); // 关键操作!
}
prom.set_value(result) 做了两件至关重要的事:
- 存储数据:将计算结果 42 写入到共享状态中。
- 通知就绪:将共享状态标记为"就绪",并唤醒任何正在等待这个 future 的线程(比如消费者线程)。
步骤 3:消费者获取值
在消费者线程(比如主线程)中,你操作 future:
cpp
// 消费者线程
int main() {
// ... 创建 promise 和 future,启动生产者线程 ...
int x = fut.get(); // 关键操作!
std::cout << "Got value: " << x << std::endl; // 输出 42
}
注意:这个fut是我们上面通过get_future()获取到的。
fut.get() 也做了两件关键的事:
- 等待就绪:如果共享状态还没就绪(即生产者还没调用 set_value),那么 get() 会阻塞当前线程,直到就绪。
- 取回数据:一旦就绪,它就从共享状态中取出值(这里是 42),然后返回。
关键点:**get() 只能调用一次。**调用后,共享状态中的数据就被取走(移动)了,future 变为无效。
示例------基本用法
- 步骤1:创建一个std::promise对象。
- 步骤2:通过这个std::promise对象获取一个std::future对象。
- 步骤3:启动一个线程,将std::promise对象传递给这个线程(或者通过其他方式让线程可以访问到这个promise)。
- 步骤4:在这个线程中执行一些计算,然后通过set_value()方法将结果设置到promise中。
- 步骤5:在原来的线程中,通过future的get()方法获取结果。
示例代码:
cpp
#include <iostream>
#include <thread>
#include <future>
void compute(std::promise<int> prom) {
// 模拟一个耗时的计算
std::this_thread::sleep_for(std::chrono::seconds(1));
int result = 42;
prom.set_value(result); // 将结果设置到promise中
}
int main() {
std::promise<int> prom; // 步骤1:创建一个std::promise对象
std::future<int> fut = prom.get_future(); // 步骤2:获取对应的future
std::thread t(compute, std::move(prom)); // 步骤3:启动线程,并将promise移动给线程
// 步骤5:在主线程中通过future获取结果
int result = fut.get(); // 这会阻塞,直到compute线程设置了值
std::cout << "Result: " << result << std::endl;
t.join();
return 0;
}
直接编译运行

等待1秒后

2.3.2.Requestor实现过程
这个代码实现了一个JSON-RPC客户端请求管理器,下面是各个函数的作用说明:
1. onResponse函数
这是核心的响应处理函数。当接收到服务器返回的响应时,系统会调用这个函数。它的主要工作是:
-
从响应消息中提取请求ID
-
根据请求ID查找之前存储的请求描述结构体
-
根据请求的原始类型(异步或回调)进行不同的处理:
-
对于异步请求,将响应结果设置到对应的promise对象中,这样等待响应的线程就能获得结果
-
对于回调请求,直接调用用户注册的回调函数处理响应
-
对于其他请求,就报错,并记录到日志里面
-
-
处理完成后,清理该请求的相关记录,防止内存泄漏
2. send函数(异步版本)
发送异步请求的函数。调用后立即返回,不阻塞当前线程。它的特点是:
-
创建一个异步类型的请求描述对象,记录请求信息
-
发送请求到服务器
-
返回一个future对象,用户可以在需要时通过这个future获取响应结果
-
适用于需要并行处理多个请求的场景
3. send函数(同步版本)
发送同步请求的函数。调用后会阻塞当前线程直到收到响应。它的工作流程是:
-
内部调用异步版本的send函数发送请求
-
获取异步版本的send函数返回的future对象,通过future.get()方法阻塞等待响应
-
收到响应后返回给调用者
-
适用于需要按顺序执行请求的逻辑
4. send函数(回调版本)
发送回调式请求的函数。请求发送后继续执行,响应到达时自动调用用户提供的回调函数。它的特点是:
-
创建一个回调类型的请求描述对象,保存回调函数
-
发送请求后立即返回
-
响应到达时,onResponse函数会自动调用用户注册的回调
-
适用于事件驱动的编程模型
同步请求本质上就是 发送请求 + 立即等待响应 的异步请求
通过调用异步请求的实现,避免了重复代码
当同步请求发生时:
同步请求 send() 被调用
内部调用异步请求 send()
异步请求 send() 创建 RequestDescribe 对象
cppRequestDescribe::ptr rdp = newDescribe(req, RType::REQ_ASYNC);这个 RequestDescribe 会被存储在
_request_desc映射表中请求发送到服务器
future.get() 阻塞等待响应
服务器响应到达,触发
onResponse()
onResponse()找到对应的 RequestDescribe,设置 promise 值future.get() 解除阻塞,返回响应
RequestDescribe 被删除
cpp
// 防止头文件重复包含
#pragma once
// 引入网络相关基础组件
#include "../common/net.hpp"
// 引入消息协议相关组件
#include "../common/message.hpp"
// 引入future头文件,用于异步编程
#include <future>
// 引入functional头文件,用于函数对象和回调
#include <functional>
// JSON-RPC命名空间
namespace jsonRpc
{
// 客户端命名空间
namespace client
{
// 请求器类,负责管理JSON-RPC请求的发送和响应处理
class Requestor
{
public:
// 智能指针别名定义
using ptr = std::shared_ptr<Requestor>;
// 请求回调函数类型定义,参数为响应消息的智能指针
using RequestCallback = std::function<void(const BaseMessage::ptr &)>;
// 异步响应类型定义,表示未来会获得的响应消息
using AsyncResponse = std::future<BaseMessage::ptr>; // 因为父类指针可以存储子类指针
// 请求描述结构体,用于跟踪每个请求的状态
struct RequestDescribe
{
// 智能指针别名
using ptr = std::shared_ptr<RequestDescribe>;
// 请求消息
BaseMessage::ptr request;
// 请求类型(同步/异步/回调)
RType rtype;
// promise对象,用于异步获取响应
std::promise<BaseMessage::ptr> response;
// 回调函数,用于回调方式处理响应
RequestCallback callback;
};
// 响应处理函数,当收到服务器的响应时被调用
// 参数conn: 连接对象,表示响应来自哪个连接
// 参数msg: 响应消息
void onResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &msg)
{
// 从响应消息中提取请求ID
std::string rid = msg->rid();
// 根据请求ID获取对应的请求描述结构体
RequestDescribe::ptr rdp = getDescribe(rid);
// 如果找不到对应的请求描述,记录错误日志
if (rdp.get() == nullptr)
{
ELOG("收到响应 - %s,但是未找到对应的请求描述!", rid.c_str());
return;
}
//获取请求描述结构体里面的信息
// 根据请求类型处理响应
if (rdp->rtype == RType::REQ_ASYNC)//这个响应对应的请求是异步请求
{
// 异步请求:将响应设置到promise中
rdp->response.set_value(msg);
}
else if (rdp->rtype == RType::REQ_CALLBACK)//这个响应对应的请求是回调请求
{
// 回调请求:如果注册了回调函数,那么执行注册的回调函数
if (rdp->callback)
{
rdp->callback(msg);
}
}
else
{
// 未知请求类型,记录错误日志
ELOG("请求类型未知!!");
}
// 处理完成后删除请求描述结构
delDescribe(rid);//根据请求ID删除掉这个请求描述结构
}
// 发送异步请求
// 参数conn: 连接对象,用于发送请求
// 参数req: 请求消息
// 参数async_rsp: 输出型参数,用于接收异步响应future对象
// 返回值: 发送是否成功
bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, AsyncResponse &async_rsp)
{
// 创建异步请求描述对象
RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_ASYNC);
// 检查请求描述对象是否创建成功
if (rdp.get() == nullptr)
{
ELOG("构造请求描述对象失败!");
return false;
}
// 发送请求
conn->send(req);
// 获取future对象,用于后续获取响应
async_rsp = rdp->response.get_future();//response可是std::promise对象,调用get_future就能获取到一个std::future对象
return true;
}
// 发送同步请求(阻塞等待响应)
// 参数conn: 连接对象,用于发送请求
// 参数req: 请求消息
// 参数rsp: 输出参数,用于接收响应消息
// 返回值: 发送和接收是否成功
bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, BaseMessage::ptr &rsp)
{
//核心思想:同步请求=异步请求+立即阻塞等待
// 用于接收异步响应的future对象
AsyncResponse rsp_future;//using AsyncResponse = std::future<BaseMessage::ptr>;
// 发送异步请求
bool ret = send(conn, req, rsp_future);//注意:异步请求会将future对象填充到rsp_future
// 检查发送是否成功
if (ret == false)
{
return false;
}
// 阻塞等待响应(future.get()会阻塞直到响应到达)
rsp = rsp_future.get();//接收异步响应的future对象
return true;
}
// 发送回调请求(响应通过回调函数处理)
// 参数conn: 连接对象,用于发送请求
// 参数req: 请求消息
// 参数cb: 回调函数,响应到达时调用
// 返回值: 发送是否成功
bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, const RequestCallback &cb)
{
// 创建回调请求的描述对象
RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_CALLBACK, cb);
// 检查请求描述对象是否创建成功
if (rdp.get() == nullptr)
{
ELOG("构造请求描述对象失败!");
return false;
}
// 发送请求
conn->send(req);
return true;
}
private:
// 创建新的请求描述对象
// 参数req: 请求消息
// 参数rtype: 请求类型
// 参数cb: 回调函数(仅对回调请求有效)
// 返回值: 请求描述对象的智能指针
RequestDescribe::ptr newDescribe(const BaseMessage::ptr &req, RType rtype,
const RequestCallback &cb = RequestCallback())
{
// 加锁保护共享数据
std::unique_lock<std::mutex> lock(_mutex);
// 创建请求描述对象
RequestDescribe::ptr rd = std::make_shared<RequestDescribe>();
// 设置请求消息
rd->request = req;
// 设置请求类型
rd->rtype = rtype;
// 如果是回调请求且有回调函数,设置回调
if (rtype == RType::REQ_CALLBACK && cb)
{
rd->callback = cb;
}
// 将请求描述存入映射表,以请求ID为键
_request_desc.insert(std::make_pair(req->rid(), rd));
return rd;
}
// 根据请求ID获取请求描述对象
// 参数rid: 请求ID
// 返回值: 请求描述对象的智能指针,如果不存在则返回空指针
RequestDescribe::ptr getDescribe(const std::string &rid)
{
// 加锁保护共享数据
std::unique_lock<std::mutex> lock(_mutex);
// 在映射表中查找请求ID
auto it = _request_desc.find(rid);
// 如果未找到,返回空指针
if (it == _request_desc.end())
{
return RequestDescribe::ptr();
}
// 返回找到的请求描述对象
return it->second;
}
// 根据请求ID删除请求描述对象
// 参数rid: 请求ID
void delDescribe(const std::string &rid)
{
// 加锁保护共享数据
std::unique_lock<std::mutex> lock(_mutex);
// 从映射表中删除指定请求ID的请求描述结构
_request_desc.erase(rid);
}
private:
// 互斥锁,用于保护_request_desc映射表的线程安全
std::mutex _mutex;
// 请求描述映射表,键为请求ID,值为对应的请求描述对象
std::unordered_map<std::string, RequestDescribe::ptr> _request_desc;
};
}
}
实现思想还是很简单的。
2.4.源代码
cpp
// 防止头文件重复包含
#pragma once
// 引入网络相关基础组件
#include "../common/net.hpp"
// 引入消息协议相关组件
#include "../common/message.hpp"
// 引入future头文件,用于异步编程
#include <future>
// 引入functional头文件,用于函数对象和回调
#include <functional>
// JSON-RPC命名空间
namespace jsonRpc
{
// 客户端命名空间
namespace client
{
// 请求器类,负责管理JSON-RPC请求的发送和响应处理
class Requestor
{
public:
// 智能指针别名定义
using ptr = std::shared_ptr<Requestor>;
// 请求回调函数类型定义,参数为响应消息的智能指针
using RequestCallback = std::function<void(const BaseMessage::ptr &)>;
// 异步响应类型定义,表示未来会获得的响应消息
using AsyncResponse = std::future<BaseMessage::ptr>; // 因为父类指针可以存储子类指针
// 请求描述结构体,用于跟踪每个请求的状态
struct RequestDescribe
{
// 智能指针别名
using ptr = std::shared_ptr<RequestDescribe>;
// 请求消息
BaseMessage::ptr request;
// 请求类型(同步/异步/回调)
RType rtype;
// promise对象,用于异步获取响应
std::promise<BaseMessage::ptr> response;
// 回调函数,用于回调方式处理响应
RequestCallback callback;
};
// 响应处理函数,当收到服务器的响应时被调用
// 参数conn: 连接对象,表示响应来自哪个连接
// 参数msg: 响应消息
void onResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &msg)
{
// 从响应消息中提取请求ID
std::string rid = msg->rid();
// 根据请求ID获取对应的请求描述结构体
RequestDescribe::ptr rdp = getDescribe(rid);
// 如果找不到对应的请求描述,记录错误日志
if (rdp.get() == nullptr)
{
ELOG("收到响应 - %s,但是未找到对应的请求描述!", rid.c_str());
return;
}
//获取请求描述结构体里面的信息
// 根据请求类型处理响应
if (rdp->rtype == RType::REQ_ASYNC)//这个响应对应的请求是异步请求
{
// 异步请求:将响应设置到promise中
rdp->response.set_value(msg);
}
else if (rdp->rtype == RType::REQ_CALLBACK)//这个响应对应的请求是回调请求
{
// 回调请求:如果注册了回调函数,那么执行注册的回调函数
if (rdp->callback)
{
rdp->callback(msg);
}
}
else
{
// 未知请求类型,记录错误日志
ELOG("请求类型未知!!");
}
// 处理完成后删除请求描述结构
delDescribe(rid);//根据请求ID删除掉这个请求描述结构
}
// 发送异步请求
// 参数conn: 连接对象,用于发送请求
// 参数req: 请求消息
// 参数async_rsp: 输出型参数,用于接收异步响应future对象
// 返回值: 发送是否成功
bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, AsyncResponse &async_rsp)
{
// 创建异步请求描述对象
RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_ASYNC);
// 检查请求描述对象是否创建成功
if (rdp.get() == nullptr)
{
ELOG("构造请求描述对象失败!");
return false;
}
// 发送请求
conn->send(req);
// 获取future对象,用于后续获取响应
async_rsp = rdp->response.get_future();//response可是std::promise对象,调用get_future就能获取到一个std::future对象
return true;
}
// 发送同步请求(阻塞等待响应)
// 参数conn: 连接对象,用于发送请求
// 参数req: 请求消息
// 参数rsp: 输出参数,用于接收响应消息
// 返回值: 发送和接收是否成功
bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, BaseMessage::ptr &rsp)
{
//核心思想:同步请求=异步请求+立即阻塞等待
// 用于接收异步响应的future对象
AsyncResponse rsp_future;//using AsyncResponse = std::future<BaseMessage::ptr>;
// 发送异步请求
bool ret = send(conn, req, rsp_future);//注意:异步请求会将future对象填充到rsp_future
// 检查发送是否成功
if (ret == false)
{
return false;
}
// 阻塞等待响应(future.get()会阻塞直到响应到达)
rsp = rsp_future.get();//接收异步响应的future对象
return true;
}
// 发送回调请求(响应通过回调函数处理)
// 参数conn: 连接对象,用于发送请求
// 参数req: 请求消息
// 参数cb: 回调函数,响应到达时调用
// 返回值: 发送是否成功
bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, const RequestCallback &cb)
{
// 创建回调请求的描述对象
RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_CALLBACK, cb);
// 检查请求描述对象是否创建成功
if (rdp.get() == nullptr)
{
ELOG("构造请求描述对象失败!");
return false;
}
// 发送请求
conn->send(req);
return true;
}
private:
// 创建新的请求描述对象
// 参数req: 请求消息
// 参数rtype: 请求类型
// 参数cb: 回调函数(仅对回调请求有效)
// 返回值: 请求描述对象的智能指针
RequestDescribe::ptr newDescribe(const BaseMessage::ptr &req, RType rtype,
const RequestCallback &cb = RequestCallback())
{
// 加锁保护共享数据
std::unique_lock<std::mutex> lock(_mutex);
// 创建请求描述对象
RequestDescribe::ptr rd = std::make_shared<RequestDescribe>();
// 设置请求消息
rd->request = req;
// 设置请求类型
rd->rtype = rtype;
// 如果是回调请求且有回调函数,设置回调
if (rtype == RType::REQ_CALLBACK && cb)
{
rd->callback = cb;
}
// 将请求描述存入映射表,以请求ID为键
_request_desc.insert(std::make_pair(req->rid(), rd));
return rd;
}
// 根据请求ID获取请求描述对象
// 参数rid: 请求ID
// 返回值: 请求描述对象的智能指针,如果不存在则返回空指针
RequestDescribe::ptr getDescribe(const std::string &rid)
{
// 加锁保护共享数据
std::unique_lock<std::mutex> lock(_mutex);
// 在映射表中查找请求ID
auto it = _request_desc.find(rid);
// 如果未找到,返回空指针
if (it == _request_desc.end())
{
return RequestDescribe::ptr();
}
// 返回找到的请求描述对象
return it->second;
}
// 根据请求ID删除请求描述对象
// 参数rid: 请求ID
void delDescribe(const std::string &rid)
{
// 加锁保护共享数据
std::unique_lock<std::mutex> lock(_mutex);
// 从映射表中删除指定请求ID的请求描述结构
_request_desc.erase(rid);
}
private:
// 互斥锁,用于保护_request_desc映射表的线程安全
std::mutex _mutex;
// 请求描述映射表,键为请求ID,值为对应的请求描述对象
std::unordered_map<std::string, RequestDescribe::ptr> _request_desc;
};
}
}
三.RpcCaller 模块设计思想
RpcCaller模块存在的意义:向⽤⼾提供进⾏rpc调⽤接口的的模块。
RpcCaller 模块作为 RPC(远程过程调用)框架的核心组件之一,其主要作用是向用户提供简洁、统一的远程服务调用能力。
该模块封装了底层网络通信、序列化及服务寻址等复杂细节,使得开发者在进行跨进程或跨网络的服务调用时,如同调用本地方法一样简单直观。
模块内部负责构建请求、向服务端发送数据,并接收和处理响应。
其中较为关键的是,为适应不同业务场景与性能要求,RpcCaller 需要支持多种调用模式:
-
同步调用:客户端发起调用后,当前线程将阻塞等待,直到收到服务端返回的响应结果后才继续执行。该方式逻辑直观,便于控制流程,适用于对实时性要求较高、且调用线程可以暂时等待的场景。
-
异步调用:客户端发起调用后立即返回,不阻塞当前线程,后续在需要获取结果时再通过 Future 或 Promise 等机制主动获取。这种方式能有效提升调用方的并发性能和资源利用率,特别适合高并发或不允许阻塞的业务逻辑。
-
回调调用:在发起调用的同时,传入一个结果处理回调函数(或监听器)。当收到服务端响应后,系统自动触发回调执行相应处理逻辑。该模式将结果处理与调用发起解耦,适用于事件驱动或响应式编程模型,有利于构建松耦合、易扩展的系统结构。
RpcCaller里面只需要3个成员函数来完成这3种RPC请求即可。
综上所述,RpcCaller 模块通过提供上述三种调用方式,兼顾了开发的便利性、系统的灵活性以及执行的高效性,从而满足不同业务场景下的远程服务调用需求。
四.RpcCaller模块实现

成员变量 _requestor
作用:核心通信组件代理
功能说明:
- Requestor类型的智能指针,在构造函数中初始化
- 所有RPC调用都委托给_requestor执行实际的消息发送和接收
- 封装了网络通信细节,使RpcCaller专注于RPC协议层面的处理
- 提供统一的接口处理同步、异步和回调三种调用模式
构造函数 RpcCaller(const Requestor::ptr &requestor)
作用:初始化RPC调用器实例
功能说明:
- 接收一个Requestor智能指针作为参数,Requestor是实际处理网络通信的核心组件
- 将传入的Requestor保存为成员变量_requestor,供后续所有RPC调用使用
- Requestor负责消息的序列化/反序列化、网络发送/接收等底层操作
cpp
// RpcCaller.hpp - JSON-RPC 客户端调用器头文件
#pragma once
#include "requestor.hpp"
namespace jsonRpc
{
namespace client
{
// RPC调用器类,提供同步、异步和回调三种RPC调用方式
class RpcCaller
{
public:
......
private:
Requestor::ptr _requestor; // 请求器对象,负责实际的网络通信
};
}
}
4.1.同步调用函数
作用:执行阻塞式RPC调用,等待并返回结果
功能说明:
- 创建一个RpcRequest消息对象,设置唯一ID、消息类型、方法名和参数
- 通过成员变量_requestor发送请求并同步等待响应(调用会阻塞直到收到响应或超时)
- 接收响应后,我们将响应将BaseMessage转换为RpcResponse类型(一定会成功)
- 检查响应状态码,只有RCode::RCODE_OK才算是调用成功,如果成功则提取响应里面的result字段并赋值给输出型参数result
- 返回布尔值表示调用是否成功,适用于需要立即获取结果的场景
cpp
// 同步RPC调用方法
// @param conn: 连接对象指针,用于指定目标连接
// @param method: 要调用的RPC方法名
// @param params: JSON格式的参数
// @param result: 用于接收返回结果的JSON对象引用
// @return: 调用成功返回true,失败返回false
bool call(const BaseConnection::ptr &conn, const std::string &method,
const Json::Value ¶ms, Json::Value &result)
{
DLOG("开始同步rpc调用...");
// 1. 组织RPC请求消息
auto req_msg = MessageFactory::create<RpcRequest>();//通过工厂类来生成一个RpcRequest对象,也就是RPC请求对象
req_msg->setId(UUID::uuid()); // 生成唯一ID
req_msg->setMType(MType::REQ_RPC); // 设置消息类型为RPC请求
req_msg->setMethod(method); // 设置方法名
req_msg->setParams(params); // 设置参数
BaseMessage::ptr rsp_msg; // 响应消息指针
// 2. 发送请求并等待响应
bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), rsp_msg);
//注意这里调用的是同步的版本,这里会阻塞等待直到服务器返回响应存储到rsp_msg
if (ret == false)
{
ELOG("同步Rpc请求失败!");
return false;
}
DLOG("收到响应,进行解析,获取结果!");
// 3. 解析响应消息
auto rpc_rsp_msg = std::dynamic_pointer_cast<RpcResponse>(rsp_msg);//将父类指针转换成子类指针,这是一定可以成功的,
//因为无论请求还是响应,我们都说通过工厂类来生成一个子类,但是却返回一个父类指针,现在我们在这里只不过是还原到最初的状态而已
if (!rpc_rsp_msg)
{
ELOG("rpc响应,向下类型转换失败!");
return false;
}
// 检查响应状态码
if (rpc_rsp_msg->rcode() != RCode::RCODE_OK)
{
ELOG("rpc请求出错:%s", errReason(rpc_rsp_msg->rcode()));
return false;
}
// 4. 获取结果并返回
result = rpc_rsp_msg->result();
DLOG("结果设置完毕!");
return true;
}
4.2. 异步调用函数
作用:执行非阻塞RPC调用,返回future对象用于稍后获取结果
功能说明:
- 创建RpcRequest消息(与同步调用相同)
- 创建promise-future对:promise用于在回调中设置值,future用于异步获取结果
- 将promise绑定到回调函数Callback,当响应到达时自动调用回调函数Callback
- 通过Requestor异步发送请求(函数立即返回,不阻塞)
- 将future对象通过引用参数返回给调用者
- 调用者可以在需要时通过future.get()或future.wait()获取结果
- 适用于需要并行处理多个RPC请求或不想阻塞主线程的场景
cpp
// 异步RPC调用方法(返回future对象)
// @param conn: 连接对象指针
// @param method: 要调用的RPC方法名
// @param params: JSON格式的参数
// @param result: 异步响应future对象引用
// @return: 调用成功返回true,失败返回false
bool call(const BaseConnection::ptr &conn, const std::string &method,
const Json::Value ¶ms, JsonAsyncResponse &result)
{
// 创建RPC请求消息
auto req_msg = MessageFactory::create<RpcRequest>();//通过工厂类来生成一个RpcRequest对象,也就是RPC请求对象
req_msg->setId(UUID::uuid()); // 生成唯一ID
req_msg->setMType(MType::REQ_RPC); // 设置消息类型
req_msg->setMethod(method); // 设置方法名
req_msg->setParams(params); // 设置参数
// 创建promise-future对,用于异步获取结果
auto json_promise = std::make_shared<std::promise<Json::Value>>();
result = json_promise->get_future(); // 获取future对象,方便后面通过get()来获取结果
// 绑定回调函数,响应到达时设置promise的值
Requestor::RequestCallback cb = std::bind(&RpcCaller::Callback,
this, json_promise, std::placeholders::_1);
//响应到达了,就自动调用RpcCaller::Callback函数,在这个函数里面,我们调用了json_promise的set_value函数,
//将我们的结果设置进了这个json_promise,这样子我们调用result的get()函数就能立刻返回了
// 发送异步请求
bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), cb);
//注意这里调用的是回调的版本,消息一到达,就执行我们传递进去的回调函数,这样子我们就能调用result的get()函数获取到这个结果了
if (ret == false)
{
ELOG("异步Rpc请求失败!");
return false;
}
return true;
}
那么在我们的回调函数Callback里面,我们干了什么东西呢?
Callback函数(私有)
作用:处理异步调用(future模式)的响应转换和结果设置
功能说明:
- 同样将BaseMessage(父类)转换为RpcResponse类型(子类)并进行错误检查
- 与Callback1的主要区别在于:它操作的是promise对象而非用户回调
- 通过输出型参数result(即std::promise类型)来调用**set_value()**将RPC响应的结果设置到result(promise类型)中
- 这使得通过该promise (也就是输出型参数result)获取的future对象 (也就是上面异步调用函数call里面的输出型参数------result对象)变为就绪状态,调用者通过这个result可以获取这次RPC响应的结果
cpp
// 回调函数2:用于处理异步调用的响应,设置promise值
// @param result: promise对象指针
// @param msg: 接收到的响应消息
void Callback(std::shared_ptr<std::promise<Json::Value>> result, const BaseMessage::ptr &msg)
{
// 将基类消息转换为RPC响应消息
auto rpc_rsp_msg = std::dynamic_pointer_cast<RpcResponse>(msg);
//将父类指针转换成子类指针,这是一定可以成功的,
//因为无论请求还是响应,我们都说通过工厂类来生成一个子类,但是却返回一个父类指针,现在我们在这里只不过是还原到最初的状态而已
if (!rpc_rsp_msg)
{
ELOG("rpc响应,向下类型转换失败!");
return;
}
// 检查响应状态码
if (rpc_rsp_msg->rcode() != RCode::RCODE_OK)//如果状态码说有错误,那么我们就没有必要再去获得结果了
{
ELOG("rpc异步请求出错:%s", errReason(rpc_rsp_msg->rcode()));
return;
}
// 设置promise的值,通知等待的future
result->set_value(rpc_rsp_msg->result());//不使用这个set_value函数的话,我们去掉用future的get方法会一直阻塞
}
4.3. 回调式调用函数
作用:执行基于回调的RPC调用,结果通过回调函数返回
功能说明:
- 创建RpcRequest消息(与前两种方式相同)
- 将用户提供的回调函数cb包装成Requestor所需的回调格式
- 通过成员变量Requestor异步发送请求
- 当RPC响应到达时,自动调用内部的Callback1函数
- Callback1函数处理响应,检查状态,然后调用用户回调函数cb并传递结果
- 适用于事件驱动或异步编程模型,代码结构更清晰
cpp
// 回调式RPC调用方法
// @param conn: 连接对象指针
// @param method: 要调用的RPC方法名
// @param params: JSON格式的参数
// @param cb: 结果回调函数
// @return: 调用成功返回true,失败返回false
bool call(const BaseConnection::ptr &conn, const std::string &method,
const Json::Value ¶ms, const JsonResponseCallback &cb)
{
// 创建RPC请求消息
auto req_msg = MessageFactory::create<RpcRequest>();
req_msg->setId(UUID::uuid()); // 生成唯一ID
req_msg->setMType(MType::REQ_RPC); // 设置消息类型
req_msg->setMethod(method); // 设置方法名
req_msg->setParams(params); // 设置参数
// 绑定回调函数,将用户回调包装成Requestor需要的格式
Requestor::RequestCallback req_cb = std::bind(&RpcCaller::Callback1,
this, cb, std::placeholders::_1);
// 发送回调式请求
bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), req_cb);//注意这里调用的是回调的版本
if (ret == false)
{
ELOG("回调Rpc请求失败!");
return false;
}
return true;
}
那么在我们的私有回调函数 Callback1里面,我们到底干了什么??
Callback1函数
作用:处理回调式调用的响应转换和错误检查
功能说明:
- 将Requestor返回的BaseMessage(父类)转换为具体的RpcResponse类型(子类)
- 进行类型安全检查,确保转换成功
- 检查RPC响应状态码,如果不是成功状态则记录错误日志
- 如果一切正常,调用用户提供的回调函数cb,将响应的结果作为参数传递
cpp
// 回调函数1:用于处理回调式调用的响应
// @param cb: 用户提供的回调函数
// @param msg: 接收到的响应消息
void Callback1(const JsonResponseCallback &cb, const BaseMessage::ptr &msg)
{
// 将基类消息转换为RPC响应消息
auto rpc_rsp_msg = std::dynamic_pointer_cast<RpcResponse>(msg);
//将父类指针转换成子类指针,这是一定可以成功的,
//因为无论请求还是响应,我们都说通过工厂类来生成一个子类,但是却返回一个父类指针,现在我们在这里只不过是还原到最初的状态而已
if (!rpc_rsp_msg)
{
ELOG("rpc响应,向下类型转换失败!");
return;
}
// 检查响应状态码
if (rpc_rsp_msg->rcode() != RCode::RCODE_OK)//响应码有问题,我们也没有必要去获取结果了
{
ELOG("rpc回调请求出错:%s", errReason(rpc_rsp_msg->rcode()));
return;
}
// 调用用户回调函数,传递结果
cb(rpc_rsp_msg->result());
}
这个思想还是很简单的吧
4.4.源代码
cpp
// RpcCaller.hpp - JSON-RPC 客户端调用器头文件
#pragma once
#include "requestor.hpp"
namespace jsonRpc
{
namespace client
{
// RPC调用器类,提供同步、异步和回调三种RPC调用方式
class RpcCaller
{
public:
// 类型别名定义
using ptr = std::shared_ptr<RpcCaller>; // 智能指针类型
using JsonAsyncResponse = std::future<Json::Value>; // 异步响应类型
using JsonResponseCallback = std::function<void(const Json::Value &)>; // 回调函数类型
// 构造函数,初始化请求器
// @param requestor: 请求器智能指针,用于发送和接收消息
RpcCaller(const Requestor::ptr &requestor) : _requestor(requestor) {}
// 同步RPC调用方法
// @param conn: 连接对象指针,用于指定目标连接
// @param method: 要调用的RPC方法名
// @param params: JSON格式的参数
// @param result: 用于接收返回结果的JSON对象引用
// @return: 调用成功返回true,失败返回false
bool call(const BaseConnection::ptr &conn, const std::string &method,
const Json::Value ¶ms, Json::Value &result)
{
DLOG("开始同步rpc调用...");
// 1. 组织RPC请求消息
auto req_msg = MessageFactory::create<RpcRequest>();//通过工厂类来生成一个RpcRequest对象,也就是RPC请求对象
req_msg->setId(UUID::uuid()); // 生成唯一ID
req_msg->setMType(MType::REQ_RPC); // 设置消息类型为RPC请求
req_msg->setMethod(method); // 设置方法名
req_msg->setParams(params); // 设置参数
BaseMessage::ptr rsp_msg; // 响应消息指针
// 2. 发送请求并等待响应
bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), rsp_msg);
//注意这里调用的是同步的版本,这里会阻塞等待直到服务器返回响应存储到rsp_msg
if (ret == false)
{
ELOG("同步Rpc请求失败!");
return false;
}
DLOG("收到响应,进行解析,获取结果!");
// 3. 解析响应消息
auto rpc_rsp_msg = std::dynamic_pointer_cast<RpcResponse>(rsp_msg);//将父类指针转换成子类指针,这是一定可以成功的,
//因为无论请求还是响应,我们都说通过工厂类来生成一个子类,但是却返回一个父类指针,现在我们在这里只不过是还原到最初的状态而已
if (!rpc_rsp_msg)
{
ELOG("rpc响应,向下类型转换失败!");
return false;
}
// 检查响应状态码
if (rpc_rsp_msg->rcode() != RCode::RCODE_OK)
{
ELOG("rpc请求出错:%s", errReason(rpc_rsp_msg->rcode()));
return false;
}
// 4. 获取结果并返回
result = rpc_rsp_msg->result();
DLOG("结果设置完毕!");
return true;
}
// 异步RPC调用方法(返回future对象)
// @param conn: 连接对象指针
// @param method: 要调用的RPC方法名
// @param params: JSON格式的参数
// @param result: 异步响应future对象引用
// @return: 调用成功返回true,失败返回false
bool call(const BaseConnection::ptr &conn, const std::string &method,
const Json::Value ¶ms, JsonAsyncResponse &result)
{
// 创建RPC请求消息
auto req_msg = MessageFactory::create<RpcRequest>();//通过工厂类来生成一个RpcRequest对象,也就是RPC请求对象
req_msg->setId(UUID::uuid()); // 生成唯一ID
req_msg->setMType(MType::REQ_RPC); // 设置消息类型
req_msg->setMethod(method); // 设置方法名
req_msg->setParams(params); // 设置参数
// 创建promise-future对,用于异步获取结果
auto json_promise = std::make_shared<std::promise<Json::Value>>();
result = json_promise->get_future(); // 获取future对象,方便后面通过get()来获取结果
// 绑定回调函数,响应到达时设置promise的值
Requestor::RequestCallback cb = std::bind(&RpcCaller::Callback,
this, json_promise, std::placeholders::_1);
//响应到达了,就自动调用RpcCaller::Callback函数,在这个函数里面,我们调用了json_promise的set_value函数,
//将我们的结果设置进了这个json_promise,这样子我们调用result的get()函数就能立刻返回了
// 发送异步请求
bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), cb);
//注意这里调用的是回调的版本,消息一到达,就执行我们传递进去的回调函数,这样子我们就能调用result的get()函数获取到这个结果了
if (ret == false)
{
ELOG("异步Rpc请求失败!");
return false;
}
return true;
}
// 回调式RPC调用方法
// @param conn: 连接对象指针
// @param method: 要调用的RPC方法名
// @param params: JSON格式的参数
// @param cb: 结果回调函数
// @return: 调用成功返回true,失败返回false
bool call(const BaseConnection::ptr &conn, const std::string &method,
const Json::Value ¶ms, const JsonResponseCallback &cb)
{
// 创建RPC请求消息
auto req_msg = MessageFactory::create<RpcRequest>();
req_msg->setId(UUID::uuid()); // 生成唯一ID
req_msg->setMType(MType::REQ_RPC); // 设置消息类型
req_msg->setMethod(method); // 设置方法名
req_msg->setParams(params); // 设置参数
// 绑定回调函数,将用户回调包装成Requestor需要的格式
Requestor::RequestCallback req_cb = std::bind(&RpcCaller::Callback1,
this, cb, std::placeholders::_1);
// 发送回调式请求
bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), req_cb);//注意这里调用的是回调的版本
if (ret == false)
{
ELOG("回调Rpc请求失败!");
return false;
}
return true;
}
private:
// 回调函数1:用于处理回调式调用的响应
// @param cb: 用户提供的回调函数
// @param msg: 接收到的响应消息
void Callback1(const JsonResponseCallback &cb, const BaseMessage::ptr &msg)
{
// 将基类消息转换为RPC响应消息
auto rpc_rsp_msg = std::dynamic_pointer_cast<RpcResponse>(msg);
//将父类指针转换成子类指针,这是一定可以成功的,
//因为无论请求还是响应,我们都说通过工厂类来生成一个子类,但是却返回一个父类指针,现在我们在这里只不过是还原到最初的状态而已
if (!rpc_rsp_msg)
{
ELOG("rpc响应,向下类型转换失败!");
return;
}
// 检查响应状态码
if (rpc_rsp_msg->rcode() != RCode::RCODE_OK)//响应码有问题,我们也没有必要去获取结果了
{
ELOG("rpc回调请求出错:%s", errReason(rpc_rsp_msg->rcode()));
return;
}
// 调用用户回调函数,传递结果
cb(rpc_rsp_msg->result());
}
// 回调函数2:用于处理异步调用的响应,设置promise值
// @param result: promise对象指针
// @param msg: 接收到的响应消息
void Callback(std::shared_ptr<std::promise<Json::Value>> result, const BaseMessage::ptr &msg)
{
// 将基类消息转换为RPC响应消息
auto rpc_rsp_msg = std::dynamic_pointer_cast<RpcResponse>(msg);
//将父类指针转换成子类指针,这是一定可以成功的,
//因为无论请求还是响应,我们都说通过工厂类来生成一个子类,但是却返回一个父类指针,现在我们在这里只不过是还原到最初的状态而已
if (!rpc_rsp_msg)
{
ELOG("rpc响应,向下类型转换失败!");
return;
}
// 检查响应状态码
if (rpc_rsp_msg->rcode() != RCode::RCODE_OK)//如果状态码说有错误,那么我们就没有必要再去获得结果了
{
ELOG("rpc异步请求出错:%s", errReason(rpc_rsp_msg->rcode()));
return;
}
// 设置promise的值,通知等待的future
result->set_value(rpc_rsp_msg->result());//不使用这个set_value函数的话,我们去掉用future的get方法会一直阻塞
}
private:
Requestor::ptr _requestor; // 请求器对象,负责实际的网络通信
};
}
}