当前位置: 首页 > article >正文

从零实现Json-Rpc框架】- 项目实现 - 基于Dispatcher模块的RPC框架

📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述

文章目录

  • 📢前言
  • 🏳️‍🌈一、基于 dispatcher 的 RPC 框架逻辑梳理
    • 1.1 模块职责划分
    • 1.2 核心流程解析
    • 1.​3 详细交互流程
  • 🏳️‍🌈二、服务端 RpcRouter 实现
    • 2.1 模块职责划分
    • 2.2 VType:JSON 值类型枚举
    • 2.3 ServiceDescribe:服务描述元数据
    • 2.4 SDescribeFactory:建造者模式创建服务描述
    • 2.5 ServiceManager:服务注册与管理
    • 2.6 RpcRouter:RPC 请求路由处理
    • 2.7 RpcRouter 整体代码
  • 🏳️‍🌈三、客户端 Requestor 实现
    • 3.1 模块职责划分
    • 3.2 RequestDescribe:请求描述元数据
    • 3.3 onResponse 方法
    • 3.4 send 方法
      • 3.4.1 同步发送(阻塞等待结果)​
      • 3.4.2 异步 Future 发送(非阻塞,返回 future)​
      • 3.4.3 异步回调发送(非阻塞,触发回调函数)​
    • 3.5 增查删方法
    • 3.6 Requestor 整体代码
  • 🏳️‍🌈四、客户端 RpcCaller 方法
    • 2.1 模块职责划分
    • 2.2 call 同步调用(阻塞等待结果)​
    • 2.3 call 异步 Future 调用(非阻塞,通过 future 获取结果)​
    • 2.4 call 异步回调调用(非阻塞,通过回调函数处理结果)​
    • 2.5 RpcCaller 整体代码
  • 👥总结


📢前言

前几篇文章中,笔者介绍了rpc的原理和目的,也介绍了需要使用的部分第三方库和我们所需实现的功能

现在我们着手项目实现篇章,目前零碎接口项目消息字段类型抽象层的封装 都已经完成了

截至上一篇文章,我们已经进行到 Dispatcher 路由的封装了

根据之前的讨论,dispatcher 负责消息的分发和处理

再根据rpc框架的实现模式

graph TDA[客户端] -->|1. 发起 RPC 调用| B(RpcCaller)B -->|2. 发送 RpcRequest| C[Requestor]C -->|3. 网络传输| D[服务端 Dispatcher]D -->|4. 路由到 RpcRouter| E[RpcRouter]E -->|5. 参数校验| F[ServiceManager]F -->|6. 调用业务回调| G[ServiceDescribe]G -->|7. 返回 RpcResponse| EE -->|8. 返回响应| CC -->|9. 处理结果| AH[客户端] -->|发布主题消息| I[TopicRequest]I --> J[服务端 Dispatcher]J -->|广播到订阅者| K[订阅同一主题的客户端]

在实现了 dispatcher 的封装后,我们还需要对 业务层 的主要功能进行封装

  1. RPC 调用(核心请求-响应机制)
  2. 主题(消息发布-订阅机制)​
  3. 服务管理(服务注册、发现、治理)​

这一篇,我们将对RPC 调用核心请求-响应机制)进行封装

在这里插入图片描述


🏳️‍🌈一、基于 dispatcher 的 RPC 框架逻辑梳理

1.1 模块职责划分

在这里插入图片描述

1.2 核心流程解析

(1) 服务端流程RpcRouter + Dispatcher)​

  1. 注册服务
    通过 SDescribeFactory 创建服务描述(参数校验规则、回调函数等)。
    调用 RpcRouter::registerMethod 将服务注册到 ServiceManager
  2. 处理请求
    客户端发送 RpcRequest服务端 Dispatcher 根据消息类型 REQ_RPC 调用 RpcRouter::onRpcRequest
    RpcRouter 查询服务描述 → 参数校验 → 调用业务回调 → 组织 RpcResponse 并发送。
// 伪代码:服务端处理流程
dispatcher->registerHandler<RpcRequest>(MType::REQ_RPC, [&](conn, req) {rpc_router->onRpcRequest(conn, req); // 由 Dispatcher 触发
});

(2) 客户端流程Requestor + RpcCaller + Dispatcher

  1. ​发送请求
    用户通过 RpcCaller 发送请求(同步/异步)。
    RpcCaller 创建 RpcRequest → 调用 Requestor::send 发送。
  2. ​接收响应
    服务端返回 RpcResponse客户端 Dispatcher 根据消息类型 RSP_RPC 调用 Requestor::onResponse
    Requestor 根据请求 ID 找到对应 RequestDescribe → 设置 promise 或触发回调。
// 伪代码:客户端处理流程
dispatcher->registerHandler<RpcResponse>(MType::RSP_RPC, [&](conn, rsp) {requestor->onResponse(conn, rsp); // 由 Dispatcher 触发
});

1.​3 详细交互流程

同步调用示例

sequenceDiagramparticipant Client as 客户端 (RpcCaller)participant Requestor as Requestorparticipant Dispatcher as Dispatcherparticipant Server as 服务端 (RpcRouter)Client->>Requestor: call(同步)Requestor->>+Dispatcher: 发送 RpcRequestDispatcher->>Server: 路由到 RpcRouter::onRpcRequestServer->>Server: 参数校验 → 业务处理Server->>Dispatcher: 返回 RpcResponseDispatcher->>Requestor: 触发 Requestor::onResponseRequestor->>Requestor: 设置 future 值Requestor->>-Client: 返回结果

​异步 Future 调用示例

sequenceDiagramparticipant Client as 客户端 (RpcCaller)participant Requestor as Requestorparticipant Dispatcher as Dispatcherparticipant Server as 服务端 (RpcRouter)Client->>Requestor: call(异步 Future)Requestor->>+Dispatcher: 发送 RpcRequestDispatcher->>Server: 路由到 RpcRouter::onRpcRequestServer->>Server: 参数校验 → 业务处理Server->>Dispatcher: 返回 RpcResponseDispatcher->>Requestor: 触发 Requestor::onResponseRequestor->>Requestor: 设置 promise 值Client->>Client: 通过 future.get() 获取结果

异步回调调用示例

sequenceDiagramparticipant Client as 客户端 (RpcCaller)participant Requestor as Requestorparticipant Dispatcher as Dispatcherparticipant Server as 服务端 (RpcRouter)Client->>Requestor: call(异步回调)Requestor->>+Dispatcher: 发送 RpcRequestDispatcher->>Server: 路由到 RpcRouter::onRpcRequestServer->>Server: 参数校验 → 业务处理Server->>Dispatcher: 返回 RpcResponseDispatcher->>Requestor: 触发 Requestor::onResponseRequestor->>Client: 调用用户回调函数

通过 Dispatcher 实现消息分发中枢,RpcRouterRequestor 分别处理 服务端 和 客户端 的 RPC 协议逻辑,代码结构清晰且扩展性强。

🏳️‍🌈二、服务端 RpcRouter 实现

2.1 模块职责划分

先来看看最后需要实现哪些部分
在这里插入图片描述

  • VType:JSON 值类型枚举
  • ServiceDescribe:服务描述元数据
  • SDescribeFactory:建造者模式创建服务描述
  • ServiceManager:服务注册与管理
  • RpcRouter:RPC 请求路由处理

** 完整请求处理流程**

  1. 客户端发送 RpcRequest,包含方法名 method、参数 params 和唯一 ID
  2. DispatcherRpcRequest 路由到 RpcRouter::onRpcRequest
  3. 服务查找与校验
// 服务存在性检查
auto service = _service_manager->select("add");
// 参数合法性检查
if (!service->paramcheck({ {"num1", 10}, {"num2", 20} }))...
  1. ​业务处理
// 回调用户定义的业务函数
_callback(params, result); 
// params示例:{ "num1": 10, "num2": 20 }, result示例:30
  1. 组织 RpcResponse 并发送回 客户端

2.2 VType:JSON 值类型枚举

enum class VType{BOOLEAN = 0,INTEGRAL,NUMERIC,STRING,ARRAY,OBJECT};

用途:定义 JSON 数据类型的枚举,用于参数校验。
​关键点:每个类型对应 Json::Value 的检查方法(如 isBool())。

2.3 ServiceDescribe:服务描述元数据

主要目的

  1. 检查参数是否存在且类型匹配
  2. 调用业务回调接口进行业务处理

成员变量

在这里插入图片描述
关键方法

  1. paramcheck(const Json::Value& params) 检查参数是否存在且类型匹配
// 判断 json值 类型是否符合预期
bool check(VType vtype, const Json::Value& val) {switch (vtype) {case VType::BOOLEAN:return val.isBool();case VType::INTEGRAL:return val.isInt();case VType::NUMERIC:return val.isNumeric();case VType::STRING:return val.isString();case VType::ARRAY:return val.isArray();case VType::OBJECT:return val.isObject();default:return false;}
}// 判断 json值 类型是否符合预期
bool rtypeCheck(const Json::Value& val) { return check(_return_type, val); }// 进行参数校验
bool paramcheck(const Json::Value& params) {// 对 params 进行参数校验for (auto desc : _params_desc) {// 判断参数是否存在if (params.isMember(desc.first) == false) {ELOG("参数字段完整性校验失败,缺少字段:%s", desc.first.c_str())return false;}// 判断参数类型是否符合预期if (check(desc.second, params[desc.first]) == false) {ELOG("参数字段类型校验失败,字段:%s", desc.first.c_str())return false;}}DLOG("参数字段完整性校验成功");return true;
}
  1. call(const Json::Value& params, Json::Value& result) 调用业务回调接口进行业务处理
// 调用业务回调接口进行业务处理
bool call(const Json::Value& params, Json::Value& result) {// 构建回调方法// 前者放入参数,后者得到结果_callback(params, result);if (rtypeCheck(result) == false) {ELOG("回调处理函数中的相应信息校验失败!");return false;}return true;
}

2.4 SDescribeFactory:建造者模式创建服务描述

其实这部分一开始是想就直接放在上面的 ServiceDescribe 部分的,但是考虑到整个部分的严谨性和独立性,还是决定单独封装一个接口用来创建相关的字段

相较于前面的工厂模式来看,这种方法称为建造者模式

  • 工厂模式:当一个类不知道它所需要的对象的类,或是当一个类希望由子类来指定创建的对象时,可以使用工厂方法。当一系列相关对象需要被创建以一起工作,并且希望避免与具体类的紧密耦合时,可以使用抽象工厂模式。
  • ​建造者模式:当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时,或者当构造过程必须允许被构造的对象有不同的表示时,适用于建造者模式。

这种模式构造出来的对象都是不可修改的,只能用来创建对象,不能修改对象内部的状态

class SDescribeFactory {
public:void setMethodName(const std::string& name) { _method_name = name; }void setReturnType(VType vtype) { _return_type = vtype; }void setParamsDesc(const std::string& pname, VType vtype) {_params_desc.push_back(ServiceDescribe::ParamsDescribe(pname, vtype));}void setCallback(const ServiceDescribe::ServiceCallback& cb) {_callback = cb;}ServiceDescribe::ptr build() {return std::make_shared<ServiceDescribe>(std::move(_method_name), std::move(_params_desc), _return_type, std::move(_callback));}private:std::string _method_name;ServiceDescribe::ServiceCallback _callback; // 实际的业务回调函数std::vector<ServiceDescribe::ParamsDescribe>_params_desc;   // 参数字段格式描述VType _return_type; // 结果作为返回值类型的描述
};

功能:分步构建 ServiceDescribe 对象,提升代码可读性。
​设计优势:避免 ServiceDescribe 构造函数参数过多。

2.5 ServiceManager:服务注册与管理

我们利用 mutex 的机制,确保在多线程环境下,能够安全地进行响应操作

// 线程安全地管理服务注册表,提供增删查功能
class ServiceManager {
public:using ptr = std::shared_ptr<ServiceManager>;// 增void insert(const ServiceDescribe::ptr& desc) {std::unique_lock<std::mutex> lock(_mutex);_services.insert(std::make_pair(desc->method(), desc));}// 查ServiceDescribe::ptr select(const std::string& method_name) {std::unique_lock<std::mutex> lock(_mutex);auto it = _services.find(method_name);if (it == _services.end()) {return ServiceDescribe::ptr();}return it->second;}// 删void remove(const std::string& method_name) {std::unique_lock<std::mutex> lock(_mutex);_services.erase(method_name);}private:std::mutex _mutex;std::unordered_map<std::string, ServiceDescribe::ptr> _services;
};

成员变量std::unordered_map<std::string, ServiceDescribe::ptr>
​职责:线程安全地管理服务注册表,提供增删查功能。

2.6 RpcRouter:RPC 请求路由处理

核心方法 onRpcRequest

// 这是注册到 dispatcher 模块针对 rpc 请求进行回调处理的业务函数
void onRpcRequest(const BaseConnection::ptr& conn, RpcRequest::ptr& req) {// 1. 查询客户端请求的方法描述 -- 判断当前服务端能否提供对应的服务auto service = _service_manager->select(req->method());if (service.get() == nullptr) {ELOG("客户端请求的方法描述不存在!", req->method().c_str())return response(conn, req, Json::Value(),fields::RCode::RCODE_NOT_FOUND_SERVICE);}// 2. 进行参数校验,确定能否提供服务if (service->paramcheck(req->params() == false)) {ELOG("客户端请求的参数校验失败!", req->method().c_str())return response(conn, req, Json::Value(),fields::RCode::RCODE_INVALID_PARAMS);}// 3. 调用业务回调接口进行业务处理Json::Value result;bool ret = service->call(req->params(), result);if (ret == false) {ELOG("客户端请求的业务处理失败!", req->method().c_str())return response(conn, req, Json::Value(),fields::RCode::RCODE_INTERNAL_ERROR);}// 4. 处理完毕得到结果,组织相应,向客户端发送return response(conn, req, result, fields::RCode::RCODE_OK);
}

流程:服务查找 → 参数校验 → 业务处理 → 返回响应

registerMethod 方法

// 注册服务描述
void registerMethod(const ServiceDescribe::ptr& service) {return _service_manager->insert(service);
}

response 方法

// 组织响应报文
void response(const BaseConnection::ptr& conn, RpcRequest::ptr& req,const Json::Value& rsp, RCode rcode) {auto msg = MessageFactory::create<RpcResponse>();msg->setId(req->rid());msg->setMType(fields::MType::RSP_RPC);msg->setRcode(rcode);msg->setResult(rsp);conn->send(msg);
}

2.7 RpcRouter 整体代码

// 业务回调函数总结
#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"namespace rpc{namespace server{enum class VType{BOOLEAN = 0,INTEGRAL,NUMERIC,STRING,ARRAY,OBJECT};class ServiceDescribe{public: using ptr = std::shared_ptr<ServiceDescribe>;using ServiceCallback = std::function<void(const Json::Value&, Json::Value&)>;using ParamsDescribe = std::pair<std::string, VType>;ServiceDescribe(std::string &&mname, std::vector<ParamsDescribe> &&desc, VType vtype, ServiceCallback &&handler) : _method_name(std::move(mname)),_callback(std::move(handler)), _params_desc(std::move(desc)), _return_type(vtype){}const std::string& method() { return _method_name; }// 进行参数校验bool paramcheck(const Json::Value& params){// 对 params 进行参数校验for(auto desc : _params_desc){// 判断参数是否存在if(params.isMember(desc.first) == false){ELOG("参数字段完整性校验失败,缺少字段:%s", desc.first.c_str())return false;}// 判断参数类型是否符合预期if(check(desc.second, params[desc.first]) == false){ELOG("参数字段类型校验失败,字段:%s", desc.first.c_str())return false;}}DLOG("参数字段完整性校验成功");return true;}// 调用业务回调接口进行业务处理bool call(const Json::Value& params, Json::Value& result){// 构建回调方法// 前者放入参数,后者得到结果_callback(params, result);if(rtypeCheck(result) == false){ELOG("回调处理函数中的相应信息校验失败!");return false;}return true;}private:// 判断 json值 类型是否符合预期bool check(VType vtype, const Json::Value& val){switch(vtype){case VType::BOOLEAN : return val.isBool();case VType::INTEGRAL : return val.isInt();case VType::NUMERIC : return val.isNumeric();case VType::STRING : return val.isString();case VType::ARRAY : return val.isArray();case VType::OBJECT : return val.isObject();default: return false;}}// 判断 json值 类型是否符合预期bool rtypeCheck(const Json::Value& val){return check(_return_type, val);}private:std::string _method_name;                   // 方法名ServiceCallback _callback;                  // 实际的业务回调函数std::vector<ParamsDescribe> _params_desc;   // 参数字段格式描述VType _return_type;                         // 结果作为返回值类型的描述};// 建造者模式// 这种模式构造出来的对象都是不可修改的,只能用来创建对象,不能修改对象内部的状态class SDescribeFactory{public:void setMethodName(const std::string& name){_method_name = name;}void setReturnType(VType vtype){_return_type = vtype;}void setParamsDesc(const std::string& pname, VType vtype){_params_desc.push_back(ServiceDescribe::ParamsDescribe(pname, vtype));}void setCallback(const ServiceDescribe::ServiceCallback& cb){_callback = cb;}ServiceDescribe::ptr build() {return std::make_shared<ServiceDescribe>(std::move(_method_name), std::move(_params_desc), _return_type, std::move(_callback));}private:std::string _method_name;ServiceDescribe::ServiceCallback _callback;                     // 实际的业务回调函数std::vector<ServiceDescribe::ParamsDescribe> _params_desc;      // 参数字段格式描述VType _return_type;                                             // 结果作为返回值类型的描述};// 线程安全地管理服务注册表,提供增删查功能class ServiceManager{public:using ptr = std::shared_ptr<ServiceManager>;// 增void insert(const ServiceDescribe::ptr& desc){std::unique_lock<std::mutex> lock(_mutex);_services.insert(std::make_pair(desc->method(), desc));} // 查ServiceDescribe::ptr select(const std::string& method_name){std::unique_lock<std::mutex> lock(_mutex);auto it = _services.find(method_name);if(it == _services.end()){return ServiceDescribe::ptr();}return it->second;}// 删void remove(const std::string& method_name){std::unique_lock<std::mutex> lock(_mutex);_services.erase(method_name);}private:std::mutex _mutex;std::unordered_map<std::string, ServiceDescribe::ptr> _services;};class RpcRouter{public:using ptr = std::shared_ptr<RpcRouter>;RpcRouter(): _service_manager(std::make_shared<ServiceManager>()){}// 这是注册到 dispatcher 模块针对 rpc 请求进行回调处理的业务函数void onRpcRequest(const BaseConnection::ptr& conn, RpcRequest::ptr& req){// 1. 查询客户端请求的方法描述 -- 判断当前服务端能否提供对应的服务auto service = _service_manager->select(req->method());if(service.get() == nullptr){ELOG("客户端请求的方法描述不存在!: %s", req->method().c_str())return response(conn, req, Json::Value(), fields::RCode::RCODE_NOT_FOUND_SERVICE); }// 2. 进行参数校验,确定能否提供服务if(service->paramcheck(req->params() == false)){ELOG("客户端请求的参数校验失败!: %s", req->method().c_str())return response(conn, req, Json::Value(), fields::RCode::RCODE_INVALID_PARAMS);}// 3. 调用业务回调接口进行业务处理Json::Value result;bool ret = service->call(req->params(), result);if(ret == false){ELOG("客户端请求的业务处理失败!: %s", req->method().c_str())return response(conn, req, Json::Value(), fields::RCode::RCODE_INTERNAL_ERROR);}// 4. 处理完毕得到结果,组织相应,向客户端发送return response(conn, req, result, fields::RCode::RCODE_OK);}// 注册服务描述void registerMethod(const ServiceDescribe::ptr& service){return _service_manager->insert(service);}private:// 组织响应报文void response(const BaseConnection::ptr& conn, RpcRequest::ptr& req, const Json::Value& rsp, RCode rcode){auto msg = MessageFactory::create<RpcResponse>();msg->setId(req->rid());msg->setMType(fields::MType::RSP_RPC);msg->setRcode(rcode);msg->setResult(rsp);conn->send(msg);}private:ServiceManager::ptr _service_manager; };}}

🏳️‍🌈三、客户端 Requestor 实现

3.1 模块职责划分

这段代码是 RPC 客户端的核心模块 Requestor负责管理请求的发送和响应的处理,支持 ​同步异步 Future异步回调 三种调用方式

方法总览

在这里插入图片描述

3.2 RequestDescribe:请求描述元数据

// 请求描述
struct RequestDescribe {using ptr = std::shared_ptr<RequestDescribe>;// 1. 请求消息// 2. 请求类型// 3. 接收响应// 4. 响应回调方法BaseMessage::ptr request;RType rtype;std::promise<BaseMessage::ptr> response;RequestCallback callback;// 1. 设置请求// 2. 设置请求类型// 3. 设置回调方法// 4. 获取异步响应void setRequest(const BaseMessage::ptr& req) { request = req; }void setRType(RType rt) { rtype = rt; }void setCallback(const RequestCallback& cb) { callback = cb; }AsyncResponse asyncResponse() { return response.get_future(); }
};

用途:存储每个请求的元数据,包括请求消息、类型、结果容器(promise)或回调函数。

3.3 onResponse 方法

// 组织响应报文
void response(const BaseConnection::ptr& conn, RpcRequest::ptr& req,const Json::Value& rsp, RCode rcode) {auto msg = MessageFactory::create<RpcResponse>();msg->setId(req->rid());msg->setMType(fields::MType::RSP_RPC);msg->setRcode(rcode);msg->setResult(rsp);conn->send(msg);
} // 对响应信息的处理回调
void onResponse(const BaseConnection::ptr& conn, BaseMessage::ptr& msg) {std::string rid = msg->rid();RequestDescribe::ptr rdp = getDescribe(rid);if (rdp.get() == nullptr) {ELOG("收到响应,但未找到对应的请求描述:%s", rid.c_str());return;}if (rdp->rtype == fields::RType::REQ_ASYNC) {rdp->response.set_value(msg);} else if (rdp->rtype == fields::RType::REQ_CALLBACK) {// 如果设置了回调函数,就调用,来处理响应if (rdp->callback)rdp->callback(msg);elseELOG("收到响应,但未设置回调函数:%s", rid.c_str());} else {ELOG("收到响应,但未知的请求类型:%d", rdp->rtype);}delDescribe(rid);
}

​关键步骤

  1. 查找请求描述:通过响应消息中的请求 ID (rid) 找到对应的 RequestDescribe
  2. 处理响应
    ​- 异步 Future:设置 promise 的值,唤醒阻塞的 future.get()
    ​- 异步回调:调用用户提供的回调函数。
  3. 清理资源:从 _request_desc 中删除已处理的请求描述。

3.4 send 方法

3.4.1 同步发送(阻塞等待结果)​

// 同步发送请求,通过 rsp 获取结果
// 发起同步 RPC 请求,调用方通过 rsp 获取响应结果。
bool send(const BaseConnection::ptr& conn, const BaseMessage::ptr& req,BaseMessage::ptr& rsp) {AsyncResponse rsp_future;bool ret = send(conn, req, rsp_future);if (ret == false) {return false;}rsp = rsp_future.get();return true;
}

流程

  1. 调用异步 Future 接口发送请求。
  2. 通过 future.get() 阻塞当前线程,直到收到响应。

适用场景:需要立即获取结果的简单调用。

3.4.2 异步 Future 发送(非阻塞,返回 future)​

// 同步发送请求,通过 rsp 获取结果
// 发起同步 RPC 请求,调用方通过 rsp 获取响应结果。
bool send(const BaseConnection::ptr& conn, const BaseMessage::ptr& req,BaseMessage::ptr& rsp) {AsyncResponsersp_future; // 异步发送请求,通过 future 获取结果// 发起异步 RPC 请求,调用方通过// async_rsp(std::future)在后续通过 get() 获取响应结果。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;}async_rsp = rdp->response.get_future();return true;}bool ret = send(conn, req, rsp_future);if (ret == false) {return false;}rsp = rsp_future.get();return true;
}

流程

  1. 创建 RequestDescribe,设置请求类型为 REQ_ASYNC
  2. 返回 future 对象,调用方后续通过 future.get() 获取结果。

适用场景:需要并行处理多个请求,灵活控制结果获取时机。

3.4.3 异步回调发送(非阻塞,触发回调函数)​

// 异步发送请求,通过回调处理结果
// 注册回调函数 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;
}

流程

  1. 创建 RequestDescribe,设置请求类型为 REQ_CALLBACK 并绑定用户回调。
  2. 发送请求,响应到达时自动触发回调。

适用场景:事件驱动模型,避免阻塞主线程。

3.5 增查删方法

// 创建新的请求描述,添加到 _request_desc 中
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;_request_desc.insert(std::make_pair(req->rid(), rd));return rd;
}// 从 _request_desc 获取请求描述
RequestDescribe::ptr getDescribe(const std::string& id) {std::unique_lock<std::mutex> lock(_mutex);auto it = _request_desc.find(id);if (it == _request_desc.end()) {ELOG("没有找到请求描述");return RequestDescribe::ptr();}return it->second;
}// 删除 _request_desc 中的请求描述
void delDescribe(const std::string& id) {std::unique_lock<std::mutex> lock(_mutex);_request_desc.erase(id);
}

3.6 Requestor 整体代码

#pragma once#include "../common/net.hpp"
#include "../common/message.hpp"
#include <future>namespace rpc{namespace client{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>;// 1. 请求消息// 2. 请求类型// 3. 接收响应// 4. 响应回调方法BaseMessage::ptr request;RType rtype;std::promise<BaseMessage::ptr> response;RequestCallback callback;// 1. 设置请求// 2. 设置请求类型// 3. 设置回调方法// 4. 获取异步响应void setRequest(const BaseMessage::ptr& req) { request = req; }void setRType(RType rt) { rtype = rt; }void setCallback(const RequestCallback& cb) { callback = cb; }AsyncResponse asyncResponse() { return response.get_future(); }};// 对响应信息的处理回调void onResponse(const BaseConnection::ptr& conn, BaseMessage::ptr& msg){std::string rid = msg->rid();RequestDescribe::ptr rdp = getDescribe(rid);if(rdp.get() == nullptr){ELOG("收到响应,但未找到对应的请求描述:%s", rid.c_str());return;}if(rdp->rtype == fields::RType::REQ_ASYNC){rdp->response.set_value(msg);}else if(rdp->rtype == fields::RType::REQ_CALLBACK){// 如果设置了回调函数,就调用,来处理响应if(rdp->callback) rdp->callback(msg);else ELOG("收到响应,但未设置回调函数:%s", rid.c_str());}else{ELOG("收到响应,但未知的请求类型:%d", rdp->rtype);}delDescribe(rid);}// 异步发送请求,通过 future 获取结果// 发起异步 RPC 请求,调用方通过 async_rsp(std::future)在后续通过 get() 获取响应结果。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;}async_rsp = rdp->response.get_future();return true;}// 同步发送请求,通过 rsp 获取结果// 发起同步 RPC 请求,调用方通过 rsp 获取响应结果。bool send(const BaseConnection::ptr& conn, const BaseMessage::ptr& req, BaseMessage::ptr& rsp){AsyncResponse rsp_future;bool ret = send(conn, req, rsp_future);if(ret == false){return false;}rsp = rsp_future.get();return true;}// 异步发送请求,通过回调处理结果// 注册回调函数 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:// 创建新的请求描述,添加到 _request_desc 中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;_request_desc.insert(std::make_pair(req->rid(), rd));return rd;}// 从 _request_desc 获取请求描述RequestDescribe::ptr getDescribe(const std::string& id){std::unique_lock<std::mutex> lock(_mutex);auto it = _request_desc.find(id);if(it == _request_desc.end()){ELOG("没有找到请求描述");return RequestDescribe::ptr();}return it->second;}// 删除 _request_desc 中的请求描述void delDescribe(const std::string& id){std::unique_lock<std::mutex> lock(_mutex);_request_desc.erase(id);}private:std::mutex _mutex;std::unordered_map<std::string, RequestDescribe::ptr> _request_desc;};}}

🏳️‍🌈四、客户端 RpcCaller 方法

RpcCallerRPC 客户端的高层封装,负责 ​组织 RPC 请求处理响应结果,并对外提供 ​同步、异步 Future、异步回调 三种调用方式

2.1 模块职责划分

在这里插入图片描述

2.2 call 同步调用(阻塞等待结果)​

// 同步调用(阻塞等待结果)
// 发送 RPC 请求并 ​阻塞当前线程 直到收到响应,结果直接写入 result
// 参数。
bool call(const BaseConnection::ptr& conn, const std::string& method,const Json::Value& params, Json::Value& result) {// 1. 组织请求auto req_msg = MessageFactory::create<RpcRequest>();req_msg->setId(UUID::uuid());req_msg->setMType(MType::REQ_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);if (ret == false) {ELOG("同步发送请求失败");return false;}// 3. 等待响应RpcResponse::ptr 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响应,错误码:%d", static_cast<int>(rpc_rsp_msg->rcode()));return false;}result = rpc_rsp_msg->result();return true;
}

​流程

  1. ​创建请求:生成唯一请求 ID,封装方法名和参数到 RpcRequest。
  2. 发送请求:通过 Requestor::send 同步发送,阻塞直到响应返回。
  3. 解析响应:转换为 RpcResponse,检查错误码,提取 result。

适用场景:需要立即获取结果的简单调用。

2.3 call 异步 Future 调用(非阻塞,通过 future 获取结果)​

// 同步调用(阻塞等待结果)
// 发送 RPC 请求并 ​阻塞当前线程
// 直到收到响应,结果直接写入 result 参数。
bool call(const BaseConnection::ptr& conn, const std::string& method,const Json::Value& params, Json::Value& result) {// 1. 组织请求auto req_msg = MessageFactory::create<RpcRequest>();req_msg->setId(UUID::uuid());req_msg->setMType(MType::REQ_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);if (ret == false) {ELOG("同步发送请求失败");return false;}// 3. 等待响应RpcResponse::ptr 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响应,错误码:%d", static_cast<int>(rpc_rsp_msg->rcode()));return false;}result = rpc // 异步 Future 调用(非阻塞,通过 future 获取结果)// 发送 RPC 请求并立即返回,调用方通过 future// ​异步获取结果boolcall(const BaseConnection::ptr& conn, const std::string& method,const Json::Value& params, std::future<Json::Value>& result) {// 向服务器发送异步回调请求,设置回调函数,回调函数中会传入一个 promise// 对象,在回调函数中去对 promise 设置数据auto req_msg = MessageFactory::create<RpcRequest>();req_msg->setId(UUID::uuid());req_msg->setMType(MType::REQ_RPC);req_msg->setMethod(method);req_msg->setParams(params);auto json_promise = std::make_shared<std::promise<Json::Value>>();result = json_promise->get_future();Requestor::RequestCallback cb =std::bind(&RpcCaller::FutureCallback, this, json_promise,std::placeholders::_1);// 异步请求发送bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), cb);if (ret == false) {ELOG("异步发送请求失败");return false;}return true;}_rsp_msg->result();return true;
}// 处理异步 Future 调用的响应,将结果设置到 promise 中
void FutureCallback(std::shared_ptr<std::promise<Json::Value>> result,const BaseMessage::ptr& msg) {RpcResponse::ptr 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;}result->set_value(rpc_rsp_msg->result());
}

流程

  1. 创建请求:同同步调用。
  2. 绑定异步结果:通过 promise/future 传递结果。
  3. 注册回调:当响应到达时,FutureCallback 将结果设置到 promise。
  4. 返回 Future:用户通过 future.get() 异步获取结果。

​适用场景:需要并行处理多个请求,灵活控制结果获取时机。

2.4 call 异步回调调用(非阻塞,通过回调函数处理结果)​

// 异步回调调用(非阻塞,通过回调函数处理结果)​
// 发送 RPC 请求并立即返回,响应到达时 ​触发回调函数 cb
// 处理结果
bool call(const BaseConnection::ptr& conn, const std::string& method,const Json::Value& params, const JsonResponseCallback& cb) {auto req_msg = MessageFactory::create<RpcRequest>();req_msg->setId(UUID::uuid());req_msg->setMType(MType::REQ_RPC);req_msg->setMethod(method);req_msg->setParams(params);Requestor::RequestCallback req_cb =std::bind(&RpcCaller::Callback, 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;
}// 处理异步回调调用的响应,调用用户提供的回调函数 cb
void Callback(const JsonResponseCallback& cb, const BaseMessage::ptr& msg) {RpcResponse::ptr 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());
}

流程

  1. 创建请求:同同步调用。
  2. 注册用户回调:当响应到达时,Callback 解析结果并调用用户提供的回调函数。

适用场景:事件驱动模型,避免阻塞主线程。

2.5 RpcCaller 整体代码

// 请求 Rpc 请求接口
#pragma once#include "requestor.hpp"namespace rpc{namespace client{class RpcCaller{public:using ptr = std::shared_ptr<RpcCaller>;using JsonAsyncResponse = std::future<Json::Value>;using JsonResponseCallback = std::function<void(const Json::Value&)>;RpcCaller(const Requestor::ptr& requestor): _requestor(requestor){}// requestor 中的处理 send 里面的回调是针对 BaseMessage 进行处理的// 用于在 rpccaller 中针对结果的处理是针对 RpcResponse 里边的 result 进行的// 同步调用(阻塞等待结果)// 发送 RPC 请求并 ​阻塞当前线程 直到收到响应,结果直接写入 result 参数。bool call(const BaseConnection::ptr& conn, const std::string& method, const Json::Value& params, Json::Value& result){// 1. 组织请求auto req_msg = MessageFactory::create<RpcRequest>();req_msg->setId(UUID::uuid());req_msg->setMType(MType::REQ_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);if(ret == false){ELOG("同步发送请求失败");return false;}// 3. 等待响应RpcResponse::ptr 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响应,错误码:%d", static_cast<int>(rpc_rsp_msg->rcode()));return false;}result = rpc_rsp_msg->result();return true;}// 异步 Future 调用(非阻塞,通过 future 获取结果)// 发送 RPC 请求并立即返回,调用方通过 future ​异步获取结果bool call(const BaseConnection::ptr& conn, const std::string& method, const Json::Value& params, std::future<Json::Value>& result){// 向服务器发送异步回调请求,设置回调函数,回调函数中会传入一个 promise 对象,在回调函数中去对 promise 设置数据auto req_msg = MessageFactory::create<RpcRequest>();req_msg->setId(UUID::uuid());req_msg->setMType(MType::REQ_RPC);req_msg->setMethod(method);req_msg->setParams(params);auto json_promise = std::make_shared<std::promise<Json::Value>>();result = json_promise->get_future();Requestor::RequestCallback cb = std::bind(&RpcCaller::FutureCallback, this, json_promise, std::placeholders::_1);// 异步请求发送bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), cb);if(ret == false){ELOG("异步发送请求失败");return false;}return true;}// 异步回调调用(非阻塞,通过回调函数处理结果)​// 发送 RPC 请求并立即返回,响应到达时 ​触发回调函数 cb 处理结果bool call(const BaseConnection::ptr& conn, const std::string& method, const Json::Value &params, const JsonResponseCallback &cb){auto req_msg = MessageFactory::create<RpcRequest>();req_msg->setId(UUID::uuid());req_msg->setMType(MType::REQ_RPC);req_msg->setMethod(method);req_msg->setParams(params);Requestor::RequestCallback req_cb = std::bind(&RpcCaller::Callback, 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:// 处理异步 Future 调用的响应,将结果设置到 promise 中void FutureCallback(std::shared_ptr<std::promise<Json::Value>> result, const BaseMessage::ptr& msg){RpcResponse::ptr 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;}result->set_value(rpc_rsp_msg->result());}// 处理异步回调调用的响应,调用用户提供的回调函数 cbvoid Callback(const JsonResponseCallback& cb, const BaseMessage::ptr& msg){RpcResponse::ptr 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());}private:Requestor::ptr _requestor;};}
}

👥总结

本篇博文对 从零实现Json-Rpc框架】- 项目实现 - 基于Dispatcher模块的RPC框架 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述

相关文章:

从零实现Json-Rpc框架】- 项目实现 - 基于Dispatcher模块的RPC框架

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…...

kubekey -实现懒人一键部署K8S集群

kubekey -实现懒人一键部署K8S集群 操作步骤 官网&#xff1a; https://kubesphere.io/zh/ 一、执行以下命令快速创建一个 Kubernetes 集群。 Master节点 如果您访问 GitHub/Googleapis 受限&#xff0c;请登录 Linux 主机&#xff0c;执行以下命令设置下载区域。 [roottest ~]…...

Android设计模式之模板方法模式

一、定义&#xff1a; 定义一个操作中的算法的框架&#xff0c;而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 二、结构&#xff1a; AbstractClass抽象类&#xff1a;定义算法的骨架&#xff0c;包含模板方法和若干…...

李宏毅机器学习笔记(1)—机器学习基本概念+深度学习基本概念

机器学习基本概念 1、获取模型 步骤 1.1、假定未知函数 带未知参数的函数 1.2、定义损失函数 真实值&#xff1a;label MAE MSE 几率分布&#xff0c;cross-entropy? 1.3、优化 单独考虑一个参数 让损失函数最小&#xff0c;找导数为零的点 单独考虑w&#xff0c;w…...

数字IC后端项目常见问题之streamOut layermap和innovus drc violation

Q1&#xff1a;我需要将Innovus设计GDS导出到Virtuoso&#xff0c;但发现写出GDS的过程会报如下所示的警告。这里写出GDS使用的是Virtuoso (DFII) streamOut mapping文件&#xff01; Clock Gen模块Routing DRC&#xff0c;Timing分析及解决 streamOut tease.gds2 -mapFile cd…...

短剧系统开发动漫短剧系统源码开发上线小程序app教程

一、市场规模与用户增长&#xff1a;突破677亿&#xff0c;Z世代成主力 整体扩张 2025年短剧市场预计同比增长15%&#xff0c;规模达677.9亿元&#xff0c;用户规模6.62亿&#xff08;占网民59.7%&#xff09;。动漫短剧作为细分领域&#xff0c;增速显著受益于二次元文化渗透&…...

太阳能高杆路灯:照亮未来的新光

在全球能源转型进程加速以及可持续发展理念日益深入人心的背景下&#xff0c;太阳能高杆路灯作为融合新能源技术、智能控制技术与多功能集成特性的创新产品&#xff0c;正逐步革新传统路灯的格局。其不仅有效解决了传统路灯对电网供电的依赖问题&#xff0c;更为城市及乡村的照…...

《C++Linux编程进阶:从0实现muduo 》-第8讲.C++面试如何高效获取线程ID

章节重点 在C面试时&#xff0c;经常被问到如果高效获取线程ID&#xff0c;但不少同学都不知道如何回答。 重点是通过__thread关键字。 重点内容 视频讲解&#xff1a;《CLinux编程进阶&#xff1a;从0实现muduo C网络框架系列》-第8讲. C面试如何高效获取线程ID 测试获取线…...

【Tauri2】011——菜单menu(2)

前言 前面简单地创建了菜单&#xff0c;接下来就来试试菜单中的action Rust中菜单项注册action AppHandle in tauri - Rusthttps://docs.rs/tauri/2.4.0/tauri/struct.AppHandle.html#method.on_menu_event这就需要用到App或者AppHandle中的方法on_menu_event #[must_use] …...

架构设计基础系列:面向对象设计的原则

引言 面向对象设计&#xff08;Object-Oriented Design&#xff0c;OOD&#xff09;是软件开发中的重要概念&#xff0c;其核心在于通过对象、类、继承、封装和多态等机制&#xff0c;实现对现实世界问题的抽象和建模。OOD不仅有助于提高代码的可重用性、可维护性和可扩展性&a…...

UE5学习笔记 FPS游戏制作35 使用.csv配置文件

文章目录 导入.csv要求首先创建一个结构体导入配置文件读取配置 导入 .csv要求 第一行必须包含标题 第一列的内容必须不能重复&#xff0c;因为第一列会被当成行的名字&#xff0c;在数据处理中发挥类似于字典的key的作用 当前的配置文件内容如下 首先创建一个结构体 结构…...

嵌入式单片机ADC数模转换的基本方法

第一:模数转换的概述 1:模数转换的概念 一般在电路中,信号分为两种,一种是模拟信号,一种是数字信号,绝大多数传感器采集的都是模拟信号,如温度、湿度、烟雾浓度、亮度.......,但是对于计算机需要处理的数字信号,那就需要利用电路把模拟信号转换为数字信号,这个转换的…...

Web数据挖掘及其在电子商务中的研究与应用

标题:Web数据挖掘及其在电子商务中的研究与应用 内容:1.摘要 随着互联网的飞速发展&#xff0c;Web数据呈现出爆炸式增长&#xff0c;电子商务领域更是积累了海量数据。在此背景下&#xff0c;对Web数据进行有效挖掘并应用于电子商务具有重要意义。本研究旨在探索Web数据挖掘技…...

01-Docker 安装

1、安装环境介绍 安装环境&#xff1a;Linux CentOS 7 本安装教程参考Docker官方文档&#xff0c;地址如下&#xff1a;https://docs.docker.com/engine/install/centos/ 2、卸载旧版docker 首先如果系统中已经存在旧的Docker&#xff0c;则先卸载&#xff1a; yum remove do…...

Redis 的缓存雪崩、击穿、穿透及其解决办法

文章目录 Redis 的缓存雪崩、击穿、穿透及其解决办法缓存雪崩解决办法 缓存击穿解决方案 缓存穿透解决方案 Redis 的缓存雪崩、击穿、穿透及其解决办法 本篇文章回顾 Redis 当中缓存崩溃、击穿、穿透现象以及相应的解决办法&#xff0c;主要的参考资料是&#xff1a;https://w…...

使用 Selenium 构建简单高效的网页爬虫

在当今数据驱动的世界中&#xff0c;网络爬虫已成为获取网络信息的重要工具。本文将介绍如何使用 Python 和 Selenium 构建一个简单而高效的网页爬虫&#xff0c;该爬虫能够处理现代网站的动态内容&#xff0c;支持代理设置和用户配置文件。 为什么选择 Selenium&#xff1f; …...

性能比拼: Pingora vs Nginx (My NEW Favorite Proxy)

本内容是对知名性能评测博主 Anton Putra Pingora vs Nginx Performance Benchmark: My NEW Favorite Proxy! 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 介绍 在本视频中&#xff0c;我们将对比 Nginx 和 Pingora&#xff08;一个用于构建网络服务的 Rust 框架…...

Ranger一分钟

简介 Ranger Admin&#xff1a;Web UIPolicy Admin Tool&#xff1a;定义和管理策略的模块Ranger Plugins&#xff1a;HDFS、Hive、HBase、Kafka、Storm、YARNRanger UserSync&#xff1a; LDAP、Active DirectoryRanger KMS&#xff1a;管理和保护数据加密的密钥 加密密钥管理…...

STM32单片机入门学习——第5节: [3-1]GPIO输出

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难&#xff0c;但我还是想去做&#xff01; 本文写于&#xff1a;2025.04.01 STM32开发板学习——第5节&#xff1a; [3-1]GPIO输出 前言开发板说明引用解答和…...

Open GL ES ->模型矩阵、视图矩阵、投影矩阵等变换矩阵数学推导以及方法接口说明

Open GL ES 变换矩阵详解 一、坐标空间变换流程 局部空间 ->Model Matrix(模型矩阵)-> 世界空间 世界空间->View Matrix(视图矩阵)->观察空间 观察空间 ->Projection Matrix(投影矩阵)->裁剪空间 裁剪空间 ->ViewPort Transform(视口变换)>屏幕空间 …...

AI提示词:自然景区智能客服

提示描述 专为自然景区游客设计的智能客服系统&#xff0c;旨在通过人工智能技术提供实时、准确的景区信息、游览建议、安全提示和服务支持&#xff0c;提升游客的体验质量和满意度。 提示词 # Role: 自然景区智能客服## Profile: - Author: xxx - Version: 1.0 - Language: …...

c#的反射和特性

在 C# 中&#xff0c;反射&#xff08;Reflection&#xff09;和特性&#xff08;Attributes&#xff09;是两个强大的功能&#xff0c;它们在运行时提供元编程能力&#xff0c;广泛用于框架开发、对象映射和动态行为扩展。以下是对它们的详细介绍&#xff0c;包括定义、用法、…...

智能体项目实现AI对话流式返回效果

1、智能体项目里与AI大模型对话的时候&#xff0c;需要从后端的流式接口里取数据并实现打字机渲染效果。这里涉及到 Markdown 格式的渲染&#xff0c;所以需要配合 marked.js 实现&#xff0c;安装 marked.js &#xff1a; npm install marked 引用&#xff1a; import { ma…...

定时任务(python)

介绍 &#x1f9e9; 什么是“定时任务”&#xff1f; 定时任务&#xff0c;就是按照设定的时间间隔或时间点自动执行某些操作。比如&#xff1a; • 每天早上8点发通知 • 每隔10秒采集一次数据 • 每小时清理一次缓存相关使用 ✅ 最简单的方式&#xff1a;while True tim…...

Python学习第二十七天

yield关键字 yield关键字扮演着核心角色&#xff0c;主要用于处理异步数据流和请求调度。 主要作用 生成器函数&#xff1a;将方法转换为生成器&#xff0c;可以逐步产生结果而不需要一次性返回所有数据 异步处理&#xff1a;支持Scrapy的异步架构&#xff0c;提高爬取效率 …...

Docker Compose 启动jar包项目

参考文章安装Docker和Docker Compose 点击跳转 配置 创建一个文件夹存放项目例如mydata mkdir /mydata上传jar包 假设我的jar包名称为goudan.jar 编写dockerfile文件 vim app-dockerfile按键盘上的i进行编辑 # 使用jdk8 FROM openjdk:8-jre# 设置时区 上海 ENV TZAsia/Sh…...

pytorch中dataloader自定义数据集

前言 在深度学习中我们需要使用自己的数据集做训练&#xff0c;因此需要将自定义的数据和标签加载到pytorch里面的dataloader里&#xff0c;也就是自实现一个dataloader。 数据集处理 以花卉识别项目为例&#xff0c;我们分别做出图片的训练集和测试集&#xff0c;训练集的标…...

SQL Server:触发器

在 SQL Server Management Studio (SSMS) 中查看数据库触发器的方法如下&#xff1a; 方法一&#xff1a;通过对象资源管理器 连接到 SQL Server 打开 SSMS&#xff0c;连接到目标数据库所在的服务器。 定位到数据库 在左侧的 对象资源管理器 中&#xff0c;展开目标数据库&a…...

标题:利用 Rork 打造定制旅游计划应用程序:一步到位的指南

引言&#xff1a; 在数字化时代&#xff0c;旅游计划应用程序已经成为旅行者不可或缺的工具。但开发一个定制的旅游应用可能需要耗费大量时间与精力。好消息是&#xff0c;Rork 提供了一种快捷且智能的解决方案&#xff0c;让你能轻松实现创意。以下是使用 Rork 创建一个定制旅…...

WebSocket原理详解(二)

WebSocket原理详解(一)-CSDN博客 目录 1.WebSocket协议的帧数据详解 1.1.帧结构 1.2.生成数据帧 2.WebSocket协议控制帧结构详解 2.1.关闭帧 2.2.ping帧 2.3.pong帧 3.WebSocket心跳机制 1.WebSocket协议的帧数据详解 1.1.帧结构 WebSocket客户端与服务器通信的最小单…...