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

[实现Rpc] 客户端 | Requestor | RpcCaller的设计实现

目录

Requestor类的实现

框架

完善

onResponse处理回复

完整代码

RpcCaller类的实现

1. 同步调用 call

2. 异步调用 call

3. 回调调用 call


Requestor类的实现

(1)主要功能:

  • 客户端发送请求的功能,进行请求描述
  • 对服务器响应的处理机制,并对返回信息 进行对应接收

(2)具体实现:

  • 意义针对客户端的每一条请求进行管理,以便于对请求对应的响应做出合适操作。
  • 对于客户端而言,其通常是主动发起请求服务的一方。然而,在多线程网络通信中,针对多个请求进行响应时可能会存在时序问题,导致无法保证一个线程发送的请求后接收到的响应就是针对这条请求的响应,这是非常危险的情况。
  • 异步IO挑战:类似于Muduo库这种异步IO网络通信库,通常IO操作都是异步的,即发送数据是将数据放入发送缓冲区,而何时发送由底层网络库协调,并且不提供recv接口,而是连接触发可读事件后,IO读取数据完成调用处理回调进行数据处理,因此在发送请求后无法直接等待该条请求的响应。

解决方案

  • 创建请求管理模块,通过给每个请求设定一个请求ID来解决上述问题。服务端响应时会标识出响应针对的是哪个请求(即响应信息包含请求ID)。
  • 客户端无论收到 哪条请求的响应,都将数据存储入hash_map中,以请求ID作为映射,并提供获取指定请求ID响应的阻塞接口。这样,只要知道自己的请求ID,就能准确获取到想要的响应。
  • 进一步优化:可以将每个请求封装描述,添加异步future控制或设置回调函数的方式,不仅支持阻塞获取响应,也能实现异步获取响应及 回调处理响应。

框架

namespace bitrpc{
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>;//智能指针 管理};//请求 信息描述//之后 好调用 所需要的rsp函数//Dispatcher调用void onResponse(const BaseConnection::ptr &conn,BaseMessage::ptr &msg){}//异步发送bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, AsyncResponse &async_rsp){}//同步发送bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, BaseMessage::ptr &rsp) {}//回调发送bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, const RequestCallback &cb) {}private://对于 请求 描述进行CURD//增RequestDescribe::ptr newDescribe(const BaseMessage::ptr &req, RType rtype, const RequestCallback &cb = RequestCallback()) {}//查 ridRequestDescribe::ptr getDescribe(const std::string &rid){}//删void delDescribe(const std::string &rid){}private:std::mutex _mutex;std::unordered_map<std::string, RequestDescribe::ptr> _request_desc;};
}
}

完善

信息描述

struct RequestDescribe {using ptr = std::shared_ptr<RequestDescribe>;BaseMessage::ptr request;RType rtype;std::promise<BaseMessage::ptr> response;RequestCallback callback;};

对收到的响应 通过 uid ,对应上是哪个请求发出的

实现了上面的解决问题

void onResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &msg){std::string rid = msg->rid();RequestDescribe::ptr rdp = getDescribe(rid);//根据先获取msg->rid() 来进行结果的调用if (rdp.get() == nullptr) {ELOG("收到响应 - %s,但是未找到对应的请求描述!", rid.c_str());return;}if (rdp->rtype == RType::REQ_ASYNC) {rdp->response.set_value(msg);//调用 不同的接口}else if (rdp->rtype == RType::REQ_CALLBACK){if (rdp->callback) rdp->callback(msg);}else {ELOG("请求类型未知!!");}delDescribe(rid);
}
onResponse处理回复
  • onResponse方法是对收到的消息进行处理的入口点。当收到服务器的响应时,该方法会被调用来匹配相应的请求描述(RequestDescribe),并通过请求类型(RType)来决定如何处理响应:
    • 如果是 异步请求(RType::REQ_ASYNC),则通过设置std::promise的值(response.set_value(msg))来完成对应的std::future,使得调用者可以通过未来对象获取响应。
    • 如果是带有回调的请求(RType::REQ_CALLBACK),则直接调用注册的回调函数(rdp->callback(msg))来处理响应。
    • 如果请求类型未知,则记录错误日志。

onResponse方法则是 对接收到的响应进行处理,


关于 promise set_value: C++11 异步操作 future类_文档学习

send方法负责构建和发送请求,

//异步操作
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);async_rsp = rdp->response.get_future();return true;
}
//同步操作
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;
}
//回调函数
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.

前文回顾:[C++#28][多态] 两个条件 | 虚函数表 | 抽象类 | override 和 final | 重载 重写 重定义

Requestor类中的 三个重载send方法,这些方法用于通过指定的连接对象发送消息

同:

  • 基本参数:所有三个send方法都接受两个相同的基本参数:一个指向BaseConnection的智能指针conn(表示网络连接)和一个指向BaseMessage的智能指针req(表示要发送的消息)。
  • 错误处理:每个send方法在无法成功创建请求描述对象时,都会记录错误日志并返回false,指示操作失败。
  • 消息发送:无论哪种方式,最终都是通过调用conn->send(req)来执行实际的消息发送。

异:

  • send方法(有三个重载版本)用于通过网络连接conn发送请求消息req到服务器:
    • 第一个send方法接受一个AsyncResponse &async_rsp参数,用于异步发送请求并返回一个std::future对象,以便于后续获取响应。(不阻塞
    • 第二个send方法是同步的,它 等待直到接收到服务器的响应并将结果赋值给BaseMessage::ptr &rsp
    • 第三个send方法允许用户在发送请求时提供一个回调函数const RequestCallback &cb,当收到响应时会自动调用该回调进行处理。(send 后,就不管了,不会阻塞等待)

📒对比第一种和第三种方式:

  1. 结果获取方式
    • 第一种方法需调用者主动通过future.get()获取结果,可能导致阻塞。
    • 第三种方法响应到达时自动调用回调函数处理结果,无需主动获取。
  1. 编程模型
    • 第一种更接近同步编程风格,通过异步手段避免长时间阻塞。
    • 第三种是典型的异步编程模型,更适合处理并发任务和事件驱动架构。
  1. 灵活性与复杂性
    • 第一种方法直观但可能引入复杂的依赖关系管理。
    • 第三种方法灵活,尤其适合链式异步操作,但可能导致“回调地狱”,增加代码维护难度。

<id,请求 desc> CURD

private:// 对于 请求 描述进行CURD//增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->GetId(), rd));//将id 和描述 进行对应return rd;}//查RequestDescribe::ptr getDescribe(const std::string &rid){std::unique_lock<std::mutex> lock(_mutex);auto it = _request_desc.find(rid);if (it == _request_desc.end()){return RequestDescribe::ptr();}return it->second;}//删void delDescribe(const std::string &rid){std::unique_lock<std::mutex> lock(_mutex);_request_desc.erase(rid);}

完整代码

#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <future> //异步操作
#include <functional> //1.灵活的函数使用 bind functionnamespace bitrpc{
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>;BaseMessage::ptr request;RType rtype;std::promise<BaseMessage::ptr> response;RequestCallback callback;};//请求 信息描述//之后 好调用 所需要的rsp函数//Dispatcher 给RSP_RPC回复调用的void onResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &msg){std::string rid = msg->GetId();RequestDescribe::ptr rdp = getDescribe(rid);if (rdp.get() == nullptr) {ELOG("收到响应 - %s,但是未找到对应的请求描述!", rid.c_str());return;}if (rdp->rtype == RType::REQ_ASYNC) {rdp->response.set_value(msg);}else if (rdp->rtype == RType::REQ_CALLBACK){if (rdp->callback) rdp->callback(msg);}else {ELOG("请求类型未知!!");}delDescribe(rid);}
//!!!!!!!对收到 的回复请求 进行id存储//异步详可见demo中的 使用//异步操作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);async_rsp = rdp->response.get_future();return true;}//同步操作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;}//回调函数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:// <id,请求 desc> CURD//增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->GetId(), rd));//将id 和描述 进行对应return rd;}//查RequestDescribe::ptr getDescribe(const std::string &rid){std::unique_lock<std::mutex> lock(_mutex);auto it = _request_desc.find(rid);if (it == _request_desc.end()){return RequestDescribe::ptr();}return it->second;}//删void delDescribe(const std::string &rid){std::unique_lock<std::mutex> lock(_mutex);_request_desc.erase(rid);}private:std::mutex _mutex;std::unordered_map<std::string, RequestDescribe::ptr> _request_desc;};
}
}

RpcCaller类的实现

(Requestor 的处理 调用 RpcCaller)

(1)主要功能:

  • 给Requestor() 提供接口。

(2)具体实现:

  • 意义:向用户提供进行RPC调用的模块。这个模块相对简单,主要功能是向外提供几个RPC调用接口,内部实现向服务端发送请求并等待获取结果。
  • 调用方式
    1. 同步调用:发起调用后,等到收到响应结果后返回。
    2. 异步调用:发起调用后立即返回,可以在需要的时候获取结果。
    3. 回调调用:发起调用的同时设置结果的处理回调,收到响应后自动对结果进行回调处理。

❗❗❗❗ 

// requestor中的处理是针对BaseMessage进行处理的
// 用于在rpccaller中针对结果的处理是针对 RpcResponse里边的result进行的
#pragma once
#include "requestor.hpp"// request 有 rpc topic server
// 其中 rpc部分的 调用函数的 实现namespace bitrpc
{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中的处理是针对BaseMessage进行处理的// 用于在rpccaller中针对结果的处理是针对 RpcResponse里边的result进行的//1.bool call(const BaseConnection::ptr &conn, const std::string &method,const Json::Value &params, Json::Value &result){DLOG("开始同步rpc调用...");// 1. 组织请求auto req_msg = MessageFactory::create<RpcRequest>();req_msg->SetId(UUID::uuid());req_msg->setMethod(method);req_msg->setParams(params);req_msg->SetMType(MType::REQ_RPC);BaseMessage::ptr rsp_msg;// 2. 发送请求bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_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;}result = rpc_rsp_msg->result();DLOG("结果设置完毕!");return true;}////2.bool call(const BaseConnection::ptr &conn, const std::string &method,const Json::Value &params, JsonAsyncResponse &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::Callback,this, json_promise, std::placeholders::_1);bool ret = _requestor->send(conn, std::dynamic_pointer_cast<BaseMessage>(req_msg), cb);if (ret == false){ELOG("异步Rpc请求失败!");return false;}return true;}//3.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::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:void Callback1(const JsonResponseCallback &cb, const BaseMessage::ptr &msg){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());}void Callback(std::shared_ptr<std::promise<Json::Value>> result, const BaseMessage::ptr &msg){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;}result->set_value(rpc_rsp_msg->result());}private:Requestor::ptr _requestor;};}
}

RpcCaller类中 三个重载的call方法,这些方法提供了不同的方式来发起RPC调用,并处理从服务器返回的响应。每个call方法根据其参数和使用场景的不同,具有特定的功能和适用性:

1. 同步调用 call

  • 目的:同步地发起一个RPC请求,并等待直到收到服务器的响应。
  • 实现
    • 创建并配置一个RpcRequest消息。
    • 使用_requestor->send()发送请求,并阻塞等待响应。
    • 接收到响应后解析结果,并检查是否有错误发生。
    • 将结果设置到输出参数result中 返回给调用者。
  • 应用场景:适用于需要立即获取结果且可以接受当前线程被阻塞的情况。

2. 异步调用 call

  • 目的:异步地发起一个RPC请求,不阻塞当前线程,允许后续通过std::future机制获取结果。
  • 实现
    • 创建并配置一个RpcRequest消息。
    • 绑定一个回调函数Callback用于在接收到响应时设置std::promise的值。
    • 使用_requestor->send()发送请求,并立即返回(非阻塞)。
    • 结果 可以通过result.get()在未来某个时刻获取。
  • 应用场景:适合于那些希望避免阻塞主线程,但仍然需要明确获取结果的场景。

3. 回调调用 call

  • 目的:异步发起RPC请求并在接收到响应时自动调用用户提供的回调函数进行处理。
  • 实现
    • 创建并配置一个RpcRequest消息。
    • 绑定一个回调函数Callback1,该回调会在接收到响应时被调用,并进一步调用用户提供的回调函数处理结果。
    • 使用_requestor->send()发送请求,并立即返回(非阻塞)。
  • 应用场景:适用于不需要立即处理响应结果,到了 就回调 来处理响应数据的场景。

本节重点,通过 重载 来实现同步 回调 异步

  • 同步:阻塞 返回结果参数
  • 回调:非阻塞 到了就返回结果
  • 异步:非阻塞 .get()获取

相关文章:

[实现Rpc] 客户端 | Requestor | RpcCaller的设计实现

目录 Requestor类的实现 框架 完善 onResponse处理回复 完整代码 RpcCaller类的实现 1. 同步调用 call 2. 异步调用 call 3. 回调调用 call Requestor类的实现 &#xff08;1&#xff09;主要功能&#xff1a; 客户端发送请求的功能&#xff0c;进行请求描述对服务器…...

flutter: table calendar笔记

pub dev&#xff1a;table_calendar 3.2.0 我来详细解释 TableCalendar 是如何根据不同的 CalendarFormat 来显示界面的。主要逻辑在 CalendarCore 中实现。 核心逻辑分为以下几个部分&#xff1a; 页面数量计算 - _getPageCount 方法根据不同格式计算总页数&#xff1a; in…...

smolagents学习笔记系列(五)Tools-in-depth-guide

这篇文章锁定官网教程中的 Tools-in-depth-guide 章节&#xff0c;主要介绍了如何详细构造自己的Tools&#xff0c;在之前的博文 smolagents学习笔记系列&#xff08;二&#xff09;Agents - Guided tour 中我初步介绍了下如何将一个函数或一个类声明成 smolagents 的工具&…...

axios几种请求类型的格式

Axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;广泛用于浏览器和 Node.js 中发送 HTTP 请求。它支持多种请求格式&#xff0c;包括 GET、POST、PUT、DELETE 等。也叫RESTful 目录 一、axios几种请求类型的格式 1、get请求 2、post请求 3、put请求 4、delete请求 二…...

架构设计系列(六):缓存

一、概述 在应用对外提供服务的时候其稳定性&#xff0c;性能会受到诸多因素的影响。缓存的作用是将频繁访问的数据缓存起来&#xff0c;避免资源重复消耗&#xff0c;提升系统服务的吞吐量。 二、缓存的应用场景 2.1 客户端 HTTP响应可以被浏览器缓存。我们第一次通过HTTP请…...

个人电脑小参数GPT预训练、SFT、RLHF、蒸馏、CoT、Lora过程实践——MiniMind图文版教程

最近看到Github上开源了一个小模型的repo&#xff0c;是真正拉低LLM的学习门槛&#xff0c;让每个人都能从理解每一行代码&#xff0c; 从零开始亲手训练一个极小的语言模型。开源地址&#xff1a; GitHub - jingyaogong/minimind: &#x1f680;&#x1f680; 「大模型」2小时…...

MySQL 中的事务隔离级别有哪些?MySQL 默认的事务隔离级别是什么?为什么选择这个级别?数据库的脏读、不可重复读和幻读分别是什么?

MySQL 中的事务隔离级别有哪些&#xff1f; 1. 读未提交&#xff08;Read Uncommitted&#xff09; 特点&#xff1a;一个事务可以读取另一个事务未提交的数据。如果一个事务对数据进行了修改但尚未提交&#xff0c;其他事务仍能读取到这些未提交的修改。优缺点&#xff1a; …...

格式工厂 FormatFactory v5.18.便携版 ——多功能媒体文件转换工具

格式工厂 FormatFactory v5.18.便携版 ——多功能媒体文件转换工具 功能&#xff1a;视频 音频 图片 文档PDF格式 各种转换&#xff0c;同格式调整压缩比例&#xff0c;调整大小 特色&#xff1a;果风图标 好看; 支持多任务队列&#xff0c;完成自动关机 下载地址&#xff1…...

python爬虫学习第十一篇爬取指定类型数据

最近在学习Python爬虫的过程中&#xff0c;尝试用爬虫获取指定类型的数据。今天&#xff0c;我想和大家分享一下我的实践过程和遇到的问题。 一、实现目标 目标是从一个网站的API接口获取不同类型的食品数据。 比如&#xff0c;第一步我想获取汉堡、小食、甜品等不同类型的数…...

Android 实现 RTMP 推流:快速集成指南

简介 在 Android 设备上实现 RTMP 推流,可以用于直播、远程监控等应用场景。本文将基于 rtmp-rtsp-stream-client-java 库,介绍如何在 Android 端快速集成 RTMP 推流,包括权限管理、相机预览、推流控制等关键步骤。 步骤 1. 配置 Maven 仓库 在 settings.gradle.kts 中添…...

KafkaTool

Offset Explorer 第一次打开需要配置kafka相关配置连接 随便先启动一个Kafka(先启动zookeeper) 设置key value 记得刷新...

基于C++“简单且有效”的“数据库连接池”

前言 数据库连接池在开发中应该是很常用的一个组件&#xff0c;他可以很好的节省连接数据库的时间开销&#xff1b;本文基使用C实现了一个简单的数据库连接池&#xff0c;代码量只有400行只有&#xff0c;但是压力测试效果很好&#xff1b;欢迎收藏 关注&#xff0c;本人将会…...

简单易懂,解析Go语言中的struct结构体

目录 4. struct 结构体4.1 初始化4.2 内嵌字段4.3 可见性4.4 方法与函数4.4.1 区别4.4.2 闭包 4.5 Tag 字段标签4.5.1定义4.5.2 Tag规范4.5.3 Tag意义 4. struct 结构体 go的结构体类似于其他语言中的class&#xff0c;主要区别就是go的结构体没有继承这一概念&#xff0c;但可…...

爬虫第九篇-结束爬虫循环

最近在学习Python爬虫的过程中&#xff0c;遇到了一个很有趣的问题&#xff1a;如何优雅地结束爬虫循环&#xff1f;今天&#xff0c;我想和大家分享一下我的发现和心得。 一、爬虫循环结束的常见问题 在写爬虫时&#xff0c;我们经常会遇到这样的情况&#xff1a;当爬取到的…...

国产编辑器EverEdit - 洞察秋毫!文件比较功能!

1 文件比较 1.1 应用场景 项目开发过程中&#xff0c;可能不同的部分会由不同的人在负责&#xff0c;存在一个文件多人编辑的情况&#xff0c;用户需要寻找差异&#xff0c;并将文档进行合并&#xff0c;比较专业的文本比较工具为BeyondCompare&#xff0c;WinMerge等。   如…...

QARepVGG--含demo实现

文章目录 前言引入Demo实现总结 前言 在上一篇博文RepVGG中&#xff0c;介绍了RepVGG网络。RepVGG 作为一种高效的重参数化网络&#xff0c;通过训练时的多分支结构&#xff08;3x3卷积、1x1卷积、恒等映射&#xff09;和推理时的单分支合并&#xff0c;在精度与速度间取得了优…...

五、 Spring Framework基础:Spring Data JPA基本用法与 Repository 接口

深入解析 Spring Data JPA&#xff1a;基本用法与 Repository 接口 Spring Data JPA 是 Spring 框架中用于简化数据访问层开发的核心模块。它基于 JPA 规范&#xff0c;底层使用 Hibernate 实现&#xff0c;通过接口继承和方法命名规则&#xff0c;自动实现增删改查等常见操作…...

如何实现在Redis集群情况下,同一类数据固定保存在同一个Redis实例中

1. 使用哈希标签&#xff08;Hash Tags&#xff09; 概述 Redis Cluster使用一致性哈希算法来分配数据到不同的节点上。为了确保相同类型的数据被分配到同一个Redis实例上&#xff0c;可以利用哈希标签&#xff08;Hash Tags&#xff09;。哈希标签是指在键名中用花括号 {} 包…...

kotlin 知识点 七 泛型的高级特性

对泛型进行实化 泛型实化这个功能对于绝大多数Java 程序员来讲是非常陌生的&#xff0c;因为Java 中完全没有这个概 念。而如果我们想要深刻地理解泛型实化&#xff0c;就要先解释一下Java 的泛型擦除机制才行。 在JDK 1.5之前&#xff0c;Java 是没有泛型功能的&#xff0c;…...

Transformer LLaMA

一、Transformer Transformer&#xff1a;一种基于自注意力机制的神经网络结构&#xff0c;通过并行计算和多层特征抽取&#xff0c;有效解决了长序列依赖问题&#xff0c;实现了在自然语言处理等领域的突破。 Transformer 架构摆脱了RNNs&#xff0c;完全依靠 Attention的优…...

Qt学习 网络编程 TPC通信

一 基本网络端口 1 网络编程基本概念 通讯方式&#xff1a;信息的通讯时通过网络来进行&#xff0c;通讯方式有两种&#xff0c;TCP和UDP通信&#xff0c;TCP通讯是专用通道&#xff0c;指定某个信息只能走某个通道&#xff0c;UDP则是非专用通道&#xff0c;比如一个车队&am…...

ESP32-S3 实战指南:BOOT-KEY 按键驱动开发全解析

一、基础知识 本篇我们使用 BOOT 按键来学习一下 GPIO 功能&#xff0c;首先补充一下相关术语介绍。 1、GPIO&#xff08;General Purpose Input/Output&#xff09; GPIO 是微控制器上的通用引脚&#xff0c;既可以作为输入&#xff08;读取外部信号&#xff09;&#xff0…...

ssh配置 远程控制 远程协作 github本地配置

0.设备版本 windows11 ubuntu24.0.4 1.1 在 Linux 上启用 SSH 服务 首先&#xff0c;确保 Linux 计算机上安装并启用了 SSH 服务。 安装和启动 OpenSSH 服务&#xff08;如果未安装&#xff09; # 在终端安装 OpenSSH 服务&#xff08;如果尚未安装&#xff09; sudo apt …...

C++知识整理day9——继承(基类与派生类之间的转换、派生类的默认成员函数、多继承问题)

文章目录 1.继承的概念和定义2.基类与派生类之间的转换3.继承中的作用域4.派生类的默认成员函数5.实现一个不能被继承的类6.继承与友元7.继承与静态成员8.多继承和菱形继承问题8.1 继承分类及菱形继承8.2 虚继承 1.继承的概念和定义 概念&#xff1a; 继承(inheritance)机制是⾯…...

2024年国赛高教杯数学建模A题板凳龙闹元宵解题全过程文档及程序

2024年国赛高教杯数学建模 A题 板凳龙闹元宵 原题再现 “板凳龙”&#xff0c;又称“盘龙”&#xff0c;是浙闽地区的传统地方民俗文化活动。人们将少则几十条&#xff0c;多则上百条的板凳首尾相连&#xff0c;形成蜿蜒曲折的板凳龙。盘龙时&#xff0c;龙头在前领头&#x…...

华为认证考试证书下载步骤(纸质+电子版)

华为考试证书可以通过官方渠道下载相应的电子证书&#xff0c;部分高级认证如HCIE还支持申请纸质证书。 一、华为电子版证书申请步骤如下&#xff1a; ①访问华为培训与认证网站 打开浏览器&#xff0c;登录华为培训与认证官方网站 ②登录个人账号 在网站首页&#xff0c;点…...

[杂学笔记]工厂模式、多态、内存空间区域划分、cp指令破坏软连接问题、UDP如何实现可靠传输、滑动窗口的原理、进程与线程、线程之间的通信

目录 1.工厂模式 2.多态 3.内存空间区域划分 4.cp指令破坏软连接问题 5.UDP实现可靠传输 6.滑动窗口的原理 7.进程与线程 8.线程之间的通信 1.工厂模式 工厂模式是一种创建对象的设计模式。它提供了一种创建对象的方式&#xff0c;将对象的创建和使用分离&#xff0c;通…...

开源RAG主流框架有哪些?如何选型?

开源RAG主流框架有哪些?如何选型? 一、开源RAG框架全景图 (一)核心框架类型对比 类型典型工具技术特征适用场景传统RAGLangChain, Haystack线性流程(检索→生成)通用问答、知识库检索增强型RAGRAGFlow, AutoRAG支持重排序、多路召回优化高精度问答、复杂文档处理轻量级…...

【Android】用 chrome://inspect/#devices 调试H5页面

通常做Android开发的过程中&#xff0c;不可避免的需要遇到去与H5交互&#xff0c;甚至有时候需要去调试H5的信息。 这里分享一下Android工程里如何调试H5页面信息&#xff1a; 直接在浏览器地址栏输入 &#xff1a; chrome://inspect/#devices 直接连接手机usb,打开开发者模式…...

贪心算法精品题

1.找钱问题 本题的贪心策略在于我们希望就可能的保留作用大的5元 class Solution { public:bool lemonadeChange(vector<int>& bills) {std::map<int ,int> _map;for(auto ch:bills){if(ch 5) _map[ch];else if(ch 10){if(_map[5] 0) return false;else{_m…...