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

[实现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;进行请求描述对服务器…...

Java 大视界 -- 深度洞察 Java 大数据安全多方计算的前沿趋势与应用革新(52)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...

山东大学软件学院nosql实验三

实验题目&#xff1a; 用Java做简单查询(2学时) 实验内容 用API方式&#xff0c;做简单查询。 实验要求 在以下要求中选择至少2个&#xff0c;使用Java语言实现数据查询&#xff0c;最终把数据输出到前端界面。 &#xff08;1&#xff09;找出年龄小于20岁的所有学生 &…...

正态分布的奇妙性质:为什么奇数阶中心矩(odd central moments)为零?

正态分布的奇妙性质&#xff1a;为什么奇数阶矩为零&#xff1f; 正态分布&#xff08;Normal Distribution&#xff09;是统计学中最常见的分布之一&#xff0c;它的钟形曲线几乎无处不在&#xff0c;从身高体重到测量误差&#xff0c;都能看到它的影子。除了均值和方差这两个…...

【入门音视频】音视频基础知识

&#x1f308;前言&#x1f308; 这个系列在我学习过程中&#xff0c;对音视频知识归纳总结的笔记。因为音视频相关讲解非常稀少&#xff0c;所以我希望通过这个音视频系列&#xff0c;跟大家一起学习音视频&#xff0c;希望减少初学者在学习上的压力。同时希望也欢迎指出文章的…...

游戏引擎学习第120天

仓库:https://gitee.com/mrxiao_com/2d_game_3 上次回顾&#xff1a;周期计数代码 我们正在进行一个项目的代码优化工作&#xff0c;目标是提高性能。当前正在优化某个特定的代码片段&#xff0c;已经将其执行周期减少到48个周期。为了实现这一目标&#xff0c;我们设计了一个…...

【Qt之QQuickWidget】QML嵌入QWidget中

由于我项目开始使用Widgets,换公司后直接使用QML开发&#xff0c;没有了解过如何实现widget到qml过渡&#xff0c;恰逢面试时遇到一家公司希望从widget迁移到qml开发&#xff0c;询问相关实现&#xff0c;一时语塞&#xff0c;很尴尬&#xff0c;粗略研究并总结下。 对qwidget嵌…...

Vue 3 + Vite 项目中配置代理解决开发环境中跨域请求问题

在 Vue 3 Vite 项目中&#xff0c;配置代理是解决开发环境中跨域请求问题的常见方法。通过在 Vite 的配置文件中设置代理&#xff0c;可以将前端请求转发到后端服务器&#xff0c;从而避免浏览器的同源策略限制。 1. 创建 Vue 3 Vite 项目 首先&#xff0c;确保你已经安装了…...

Eureka、ZooKeeper 和 Nacos 之间的对比

Eureka、ZooKeeper 和 Nacos 都是分布式系统中常用的服务注册与发现工具&#xff0c;但它们的定位、功能和适用场景有所不同。作为一名开发者&#xff0c;理解它们之间的对比有助于选择合适的技术栈。以下从多个维度进行详细比较&#xff1a; 1. 基本概述 Eureka 来源&#xff…...

CSS中padding和margin属性的使用

在 HTML 中&#xff0c;padding 和 margin 是用于控制元素布局和间距的重要属性。 一、Padding&#xff08;内边距&#xff09; 定义&#xff1a;Padding 是指元素内容与元素边框之间的距离。它可以在元素内部创造出空白区域&#xff0c;使得内容不会紧贴着边框。 作用 增加元…...

【Python爬虫(49)】分布式爬虫:在新兴技术浪潮下的蜕变与展望

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…...

网络安全-系统层攻击流程及防御措施

系统层攻击流程涉及多个阶段&#xff0c;攻击者通过逐步渗透以获取控制权或窃取数据。以下是详细的流程及防御措施&#xff1a; 1. 侦察&#xff08;Reconnaissance&#xff09; 信息收集&#xff1a; 主动扫描&#xff1a;使用工具如Nmap、Masscan扫描目标IP、开放端口、服务…...

centos 7 安装python3 及pycharm远程连接方法

安装openssl 使用pip3安装 virtualenv的时候会提示WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available. 这是因为缺少openssl 2.0以上版本 解决办法&#xff1a; 一、先确认版本 openssl version 二、安…...

【llm对话系统】如何快速开发一个支持openai接口的llm server呢

核心思路&#xff1a;使用轻量级 Web 框架&#xff0c;将 OpenAI API 请求转换为你现有推理脚本的输入格式&#xff0c;并将推理脚本的输出转换为 OpenAI API 的响应格式。 快速开发步骤列表&#xff1a; 选择合适的 Web 框架 (快速 & 简单): FastAPI: Python 最佳选择&am…...

跟着柳叶刀数字健康,学习如何通过病理切片预测分子分类对预后的影响|项目复现

小罗碎碎念 项目复现 今天和大家分享一个非常具有参考价值的项目,手把手带着大家复现一篇发表在柳叶刀数字健康的文章。 花了六个小时才完成的这篇推送,信息量非常大,遇到了很多报错问题,但是解决以后的感觉是非常爽的,先给大家展示一下最终的成果——在同一张切片上,通…...

deepseek_清华大学指导手册_pdf_1-5

deepseek_清华大学指导手册_pdf_1-5 无套路&#xff0c;无需关注&#xff0c;无需登录&#xff0c;无需app&#xff0c;直接下载&#xff1a; 下载地址 文件列表&#xff1a; 001_清华大学_DeepSeek从入门到精通.pdf 002_清华大学_DeepSeek如何赋能职场应用.pdf 003_清华大学…...

数据库(MySQL)二

MySQL 六、MySQL索引视图6.1 索引底层原理6.1.1 索引hash算法6.1.2 索引二叉树算法6.1.3 索引平衡二叉树算法6.1.4 索引BTREE树算法6.1.5 普通SQL全表扫描过程 6.2 索引分类6.2.1 按数据结构层次分类6.2.2 按字段数量层次分类6.2.3 按功能逻辑层次分类&#xff08;面试题&#…...

第15届 蓝桥杯 C++编程青少组中/高级选拔赛 202401 真题答案及解析

第 1 题 【 单选题 】 表达式117 % 16 的结果是( )。 A:0 B:5 C:7 D:10 解析: % 是取模运算符,用于计算两个数相除后的余数。 计算 117 / 16,结果是 7,余数是 5。因此,117 % 16 = 5。答案: B 第 2 题 【 单选题 】 下列选项中,字符数组定义正确的是( …...

《AI大模型趣味实战》第10集:开发一个基于Mermaid的AI绘图网站

《AI大模型趣味实战》第10集&#xff1a;开发一个基于Mermaid的AI绘图网站 抱歉不按顺序出牌&#xff0c;先出一个第10集&#xff0c;第1到第9集慢慢来&#xff0c;后续也不会按顺序&#xff0c;总之凑满36集&#xff0c;可能或补充12集。 AI大模型趣味实战专栏 所有36个主题预…...

androidstudio 运行项目加载很慢,优化方法

一、Android Studio 运行项目加载缓慢可能由多种原因引起&#xff0c;以下是一些优化建议&#xff1a; 1. 升级硬件配置 内存&#xff1a;建议至少 8GB&#xff0c;16GB 或以上更佳。 SSD&#xff1a;使用 SSD 替代 HDD 以加快读写速度。 CPU&#xff1a;多核处理器有助于提…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...