【从零实现Json-Rpc框架】- 项目实现 - Dispatcher模块实现篇
📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨
文章目录
- 📢前言
- 🏳️🌈一、通信模拟
- 1.1 服务端模拟
- 1.2 客户端模拟
- 🏳️🌈二、Dispatcher 的作用与设计意义
- 2.1 核心作用
- 2.2 注册消息处理函数
- 2.2 消息分发逻辑
- 🏳️🌈三、应用场景
- 3.1 服务端
- 3.2 客户端
- 🏳️🌈补充 - dispatcher 模板化
- 客户端
- 服务端
- 👥总结
📢前言
前几篇文章中,笔者介绍了rpc
的原理和目的,也介绍了需要使用的部分第三方库
和我们所需实现的功能
现在我们着手项目实现篇章
,目前零碎接口 和 项目消息字段类型 都已经完成了
截至上一篇文章,抽象层及其封装都已经完善了
所以我们现在需要去使用这些代码,去简单实现单线程的通信传播是否可行
并由此引出 Dispatcher路由
🏳️🌈一、通信模拟
这是利用代码实现的服务端通信流程图
sequenceDiagramparticipant Clientparticipant MuduoServerparticipant LVProtocolparticipant DispatcherClient->>MuduoServer: 发送二进制数据流MuduoServer->>LVProtocol: canProcessed()检查数据完整性LVProtocol->>LVProtocol: onMessage()解析消息头LVProtocol->>MessageFactory: 创建具体消息对象MessageFactory-->>LVProtocol: 返回消息对象LVProtocol->>Dispatcher: 传递消息对象Dispatcher->>Handler: 调用注册的回调函数Handler->>MuduoServer: 生成响应消息MuduoServer->>Client: 发送响应
1.1 服务端模拟
所以我们实现服务端的流程就是
- 创建服务端 对象
- 将 对请求数据的回调函数 设置进 服务端对象 中
- 开始循环获取 客户端连接数据
- 根据 协议 判断是否合法
- 合法,根据 回调函数 获取信息,并 设置响应信息
- 发送响应信息给 客户端
void onMessage(const rpc::BaseConnection::ptr& conn, rpc::BaseMessage::ptr& msg){std::string body = msg->serialize();std::cout << body << std::endl;auto rpc_rsp = rpc::MessageFactory::create<rpc::RpcResponse>();rpc_rsp->setId("0001");rpc_rsp->setMType(fields::MType::RSP_RPC);rpc_rsp->setRcode(fields::RCode::RCODE_OK);rpc_rsp->setResult("success");conn->send(rpc_rsp);
}int main(){auto server = rpc::ServerFactory::create(9090);server->setMessageCallback(onMessage);server->start();return 0;
}
1.2 客户端模拟
- 创建客户端对象,确保它可以连接到服务端的ip和端口
- 设置其对响应数据的回调函数
- 创建与服务端之间的连接
- 设置请求数据
- 发送请求数据到服务端
- 服务端根据流程将响应数据通过连接返回
- 根据回调函数处理响应数据
void onMessage(const rpc::BaseConnection::ptr& conn, rpc::BaseMessage::ptr& msg){std::string body = msg->serialize();std::cout << body << std::endl;
}int main(){auto client = rpc::ClientFactory::create("127.0.0.1", 9090);client->setMessageCallback(onMessage);DLOG("setMessageCallback is OK");client->connect();DLOG("connect is OK");if(client->connected() == false)ELOG("client is not connected");auto rpc_req = rpc::MessageFactory::create<rpc::RpcRequest>();rpc_req->setId("0002");rpc_req->setMType(fields::MType::REQ_RPC);rpc_req->setMethod("client_method_add");DLOG("rpc_req is OK");Json::Value param;param["num1"] = 11;param["num2"] = 22;rpc_req->setParams(param);DLOG("setParams is OK");client->send(rpc_req);DLOG("send is OK");std::this_thread::sleep_for(std::chrono::seconds(10));// client->shutdown();// DLOG("shutdown is OK");return 0;
}
最终情况大约这样
🏳️🌈二、Dispatcher 的作用与设计意义
Dispatcher
的作用是作为消息分发中心,根据消息类型将消息路由到对应的处理函数,实现了解耦和灵活性。通过注册机制,可以方便地扩展支持新的消息类型,而无需修改 Dispatcher
本身的代码。线程安全通过互斥锁保证,确保在多线程环境下正确运行。
2.1 核心作用
消息路由中心:根据消息类型(MType)将接收到的消息动态分发给预先注册的处理函数。
解耦业务逻辑:分离消息接收与处理逻辑,新增消息类型时无需修改分发逻辑。
2.2 注册消息处理函数
void registerMessage(MType mtype, const MessageCallBack& handler) {std::unique_lock<std::mutex> lock(_mutex); // 线程安全_handlers[mtype] = handler; // 存储消息类型与处理函数的映射
}
功能:将特定消息类型绑定到对应的处理函数(如处理RPC请求、主题订阅等)。
线程安全:使用互斥锁保护 _handlers
,防止多线程并发修改冲突。
2.2 消息分发逻辑
void onMessage(const BaseConnection::ptr& conn, BaseMessage::ptr& msg) {std::unique_lock<std::mutex> lock(_mutex);auto it = _handlers.find(msg->mtype()); // 查找处理函数if (it != _handlers.end()) {it->second(conn, msg); // 调用注册的回调} else {ELOG("未知消息类型: %d", static_cast<int>(msg->mtype()));conn->shutdown(); // 关闭非法连接}
}
流程:
- 根据消息类型查找注册的处理函数。
- 找到则执行回调,否则记录错误并关闭连接。
健壮性:对未知消息类型强制关闭连接,防止无效或恶意请求影响系统。
🏳️🌈三、应用场景
3.1 服务端
#include "message.hpp"
#include "net.hpp"
#include "dispatcher.hpp"
#include <thread>// version - 2
// 设置Rpc响应的处理函数
void onRpcRespond(const rpc::BaseConnection::ptr& conn, rpc::BaseMessage::ptr& msg){std::cout << "收到了Rpc响应:";std::string body = msg->serialize();std::cout << body << std::endl;std::cout << "----------------------------" << std::endl;
}// 设置Topic响应的处理函数
void onTopicRespond(const rpc::BaseConnection::ptr& conn, rpc::BaseMessage::ptr& msg){std::cout << "收到了Topic响应:";std::string body = msg->serialize();std::cout << body << std::endl;std::cout << "----------------------------" << std::endl;
}int main(){auto dispatcher = std::make_shared<rpc::Dispatcher>();dispatcher->registerMessage(fields::MType::RSP_RPC, onRpcRespond); // 注册Rpc响应处理函数dispatcher->registerMessage(fields::MType::RSP_TOPIC, onTopicRespond); // 注册Topic响应处理函数auto client = rpc::ClientFactory::create("127.0.0.1", 9090);// 调用 message_cb(conn, msg) → 实际执行 dispatcher->onMessage(conn, msg)auto message_cb = std::bind(&rpc::Dispatcher::onMessage, dispatcher.get(), std::placeholders::_1, std::placeholders::_2); // 模拟发送rpc请求client->setMessageCallback(message_cb);DLOG("client setMessageCallback is OK");client->connect();DLOG("client connect is OK");if(client->connected() == false)ELOG("client is not connected");auto rpc_req = rpc::MessageFactory::create<rpc::RpcRequest>();rpc_req->setId("0001");rpc_req->setMType(fields::MType::REQ_RPC);rpc_req->setMethod("client_method_add");DLOG("rpc_req is OK");Json::Value param;param["num1"] = 11;param["num2"] = 22;rpc_req->setParams(param);DLOG("setParams is OK");client->send(rpc_req);DLOG("send is OK");// 模拟发送topic请求auto topic_req = rpc::MessageFactory::create<rpc::TopicRequest>();topic_req->setId("0002");topic_req->setMType(fields::MType::REQ_TOPIC);topic_req->setOptype(fields::TopicOptype::TOPIC_CREATE);topic_req->setTopicKey("client_topic_key");topic_req->setTopicMsg("client_topic_msg");client->send(topic_req); std::this_thread::sleep_for(std::chrono::seconds(10));// client->shutdown();// DLOG("shutdown is OK");return 0;
}
3.2 客户端
#include "message.hpp"
#include "net.hpp"
#include "dispatcher.hpp"//version - 2
// 设置PRC请求的处理函数
void onRpcRequest(const rpc::BaseConnection::ptr& conn, rpc::BaseMessage::ptr& msg){std::cout << "收到了Rpc请求:";std::string body = msg->serialize();std::cout << body << std::endl;std::cout << "----------------------------" << std::endl;auto rpc_req = rpc::MessageFactory::create<rpc::RpcResponse>();rpc_req->setId("0001");rpc_req->setMType(fields::MType::RSP_RPC);rpc_req->setRcode(fields::RCode::RCODE_OK);rpc_req->setResult("success");conn->send(rpc_req);
}// 设置主题请求的处理函数
void onTopicRequest(const rpc::BaseConnection::ptr& conn, rpc::BaseMessage::ptr& msg){std::cout << "收到了Topic请求:";std::string body = msg->serialize();std::cout << body << std::endl;std::cout << "----------------------------" << std::endl;auto topic_req = rpc::MessageFactory::create<rpc::TopicResponse>();topic_req->setId("0002");topic_req->setMType(fields::MType::RSP_TOPIC);topic_req->setRcode(fields::RCode::RCODE_OK);conn->send(topic_req);
}int main(){auto dispatcher = std::make_shared<rpc::Dispatcher>();dispatcher->registerMessage(fields::MType::REQ_RPC, onRpcRequest);dispatcher->registerMessage(fields::MType::REQ_TOPIC, onTopicRequest);auto server = rpc::ServerFactory::create(9090);auto message_cb = std::bind(&rpc::Dispatcher::onMessage, dispatcher.get(), std::placeholders::_1, std::placeholders::_2);server->setMessageCallback(message_cb);server->start();return 0;
}
🏳️🌈补充 - dispatcher 模板化
因为我们现在的下述语句中是直接传进子类给 dispatcher
的
auto dispatcher = std::make_shared<rpc::Dispatcher>();dispatcher->registerMessage(fields::MType::RSP_RPC, onRpcRespond); // 注册Rpc响应处理函数dispatcher->registerMessage(fields::MType::RSP_TOPIC, onTopicRespond); // 注册Topic响应处理函数auto client = rpc::ClientFactory::create("127.0.0.1", 9090);// 调用 message_cb(conn, msg) → 实际执行 dispatcher->onMessage(conn, msg)auto message_cb = std::bind(&rpc::Dispatcher::onMessage, dispatcher.get(), std::placeholders::_1, std::placeholders::_2);
但是我们在定义 dispatcher
时采用的是父类对象定义的,所以在调用这个方法时,会提高代码的冗余性,就比如说,每次要使用这个方法,都要判断一下他是属于哪一个子类,因此我们可以给 dispatcher
封装一个模板,提高效率
#include "message.hpp"
#include "net.hpp"// Dispatcher 类在 RPC 框架中担任 消息路由中心 的角色,负责将接收到的不同消息类型动态分发给对应的处理函数
namespace rpc{class Callback{public:using ptr = std::shared_ptr<Callback>;virtual void onMessage(const BaseConnection::ptr& conn, BaseMessage::ptr& msg) = 0;};template<typename T>class CallbackT : public Callback{public:using ptr = std::shared_ptr<CallbackT<T>>;using MessageCallBack = std::function<void(const BaseConnection::ptr& conn, std::shared_ptr<T>& msg)>;CallbackT(const MessageCallBack& handler): _handler(handler){}void onMessage(const BaseConnection::ptr& conn, BaseMessage::ptr& msg){auto type_msg = std::dynamic_pointer_cast<T>(msg);_handler(conn, type_msg);}private:MessageCallBack _handler;};class Dispatcher{public:using ptr = std::shared_ptr<Dispatcher>;// 注册消息类型与处理函数的映射template<typename T>void registerHandler(MType mtype, const typename CallbackT<T>::MessageCallBack& handler){std::unique_lock<std::mutex> lock(_mutex);auto cb = std::make_shared<CallbackT<T>>(handler);_handlers.insert(std::make_pair(mtype, cb));}// 处理消息的分发逻辑void onMessage(const BaseConnection::ptr& conn, BaseMessage::ptr& msg){// 找到消息类型对应的业务处理函数,并调用std::unique_lock<std::mutex> lock(_mutex);auto it = _handlers.find(msg->mtype());if(it != _handlers.end()){it->second->onMessage(conn, msg);}else{// 没有找到指定类型的处理回调 - 因为客户端和服务端都是我们自己设计的,因此不可能出现这种情况ELOG("收到未知类型的消息: %d!", static_cast<int>(msg->mtype()));conn->shutdown();}}private:std::mutex _mutex;std::unordered_map<MType, Callback::ptr> _handlers;};
}
当然因为 dispatcher 设置了模板,所以我们还是需要对服务端和客户端重新的相应方法改一下
客户端
两者直接传入目标消息类,不用传基类
void onRpcRespond(const rpc::BaseConnection::ptr& conn, rpc::RpcResponse::ptr& msg)
void onTopicRespond(const rpc::BaseConnection::ptr& conn, rpc::TopicResponse::ptr& msg)
使用时添加模板
dispatcher->registerHandler<rpc::RpcResponse>(fields::MType::RSP_RPC, onRpcRespond); // 注册Rpc响应处理函数
dispatcher->registerHandler<rpc::TopicResponse>(fields::MType::RSP_TOPIC, onTopicRespond); // 注册Topic响应处理函数
服务端
void onRpcRequest(const rpc::BaseConnection::ptr& conn, rpc::RpcRequest::ptr& msg)
void onTopicRequest(const rpc::BaseConnection::ptr& conn, rpc::TopicRequest::ptr& msg)
dispatcher->registerHandler<rpc::RpcRequest>(fields::MType::REQ_RPC, onRpcRequest);dispatcher->registerHandler<rpc::TopicRequest>(fields::MType::REQ_TOPIC, onTopicRequest);
👥总结
本篇博文对 【从零实现Json-Rpc框架】- 项目实现 - Dispatcher路由实现篇 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~
相关文章:
【从零实现Json-Rpc框架】- 项目实现 - Dispatcher模块实现篇
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
WPS宏开发手册——JSA语法练习
目录 系列文章3、JSA语法练习3.1、运算练习3.2、比较练习3.3、if else练习3.4、for 练习3.5、字符串、数组方法练习3.6、语义转编程练习题 系列文章 使用、工程、模块介绍 JSA语法 JSA语法练习题 Excel常用Api 后续EXCEL实战、常见问题、颜色附录,持…...
【自学笔记】Go语言基础知识点总览-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1. Go 语言简介2. 基本语法变量声明与赋值常量数据类型运算符 3. 控制结构条件语句循环语句 4. 函数函数定义与调用多返回值匿名函数与闭包 5. 并发编程goroutinech…...
PyQt6实例_批量下载pdf工具_主线程停止线程池
目录 前置: 代码: 视频: 前置: 1 本系列将以 “PyQt6实例_批量下载pdf工具”开头,放在 【PyQt6实例】 专栏 2 本系列涉及到的PyQt6知识点: 线程池:QThreadPool,QRunnable; 信号与…...
在 Vue 项目中,登录成功后是否存储 token 与用户信息到本地
答案:不安全 举例:直接使用localStorage存储,本地存储可能会被 XSS 攻击窃取 localStorage.setItem(token, response.token)localStorage.setItem(userInfo, JSON.stringify({username: response.username,email: response.email})) 推荐方…...
【加密社】做一个展示币种价格的组件
具体的代码是以下,可以看到 <div id"crypto-price-widget"><p class"loading">Loading cryptocurrency prices... <span class"spinner"></span></p> </div><script> document.addEventListener(DOM…...
CANoe入门——CANoe的诊断模块,调用CAPL进行uds诊断
目录 一、诊断窗口介绍 二、诊断数据库文件管理 三、添加基础诊断描述文件(若没有CDD/ODX/PDX文件)并使用对应的诊断功能进行UDS诊断 3.1、添加基础诊断描述文件 3.2、基于基础诊断,使用诊断控制台进行UDS诊断 3.2.1、生成基础诊断 3.…...
AI日报 - 2025年3月30日
🌟 今日概览(60秒速览) ▎🤖 模型进展 | Qwen2.5-Omni多模态实时交互,Gemini 2.5 Pro/GPT-4o低调升级,Claude内部思考过程揭秘。 新模型和升级持续涌现,多模态与内部机制理解成焦点。 ▎&#x…...
蓝桥刷题note11(好数)
1,好数 一个整数如果按从低位到高位的顺序,奇数位 (个位、百位、万位 ⋯⋯ ) 上的数字是奇数,偶数位 (十位、千位、十万位 ⋯⋯ ) 上的数字是偶数,我们就称之为 “好数”。 给定一个正整数 NN,请计算从 1 到 NN 一共…...
Go常用的设计模式
Go常用的设计模式 常见的设计模式,如 单例模式、工厂模式、策略模式、观察者模式、代理模式、装饰器模式 和 适配器模式 都可以在 Go 中实现,适用于不同的开发需求。 这些设计模式不仅能帮助你编写结构清晰、可维护的代码,还能让你更好地应…...
复现文献中的三维重建图像生成,包括训练、推理和可视化
要复现《One - 2 - 3 - 45 Fast Single Image to 3D Objects with Consistent Multi - View Generation and 3D Diffusion (CVPR)2024》文献中的三维重建图像生成,包括训练、推理和可视化,并且确保代码能正常运行,下面是基本的实现步骤和示例…...
day17 学习笔记
文章目录 前言一、数组的增删改查1.resize函数2.append函数3.insert函数4.delete函数5.argwhere函数6.unique函数 二、统计函数1.amax,amin函数2.ptp函数3.median函数4.mean函数5.average函数6.var,std函数 前言 通过今天的学习,我掌握了num…...
Mysql练习题
先创建对应数据表 #先创建表 #学生表 Student create table Student(SId varchar(10),Sname varchar(10),Sage datetime,Ssex varchar(10)); insert into Student values(01 , 赵雷 , 1990-01-01 , 男); insert into Student values(02 , 钱电 , 1990-12-21 , 男); insert int…...
torch不能使用cuda的解决方案
遇到了这样的报错,说明 torch不能使用cuda 反思 我频繁地尝试安装不同的 nvdia 驱动,浪费了很多时间。因为我的错误地认为nvidia会自带cuda,其实cuda需要单独安装。 还有我的torch是cpu版本的,即使nvidia cuda安装了࿰…...
Python 循环全解析:从语法到实战的进阶之路
一、问答题 (1)下面的循环体被重复了多少次?每次循环的输出结果是什么? i1 while i < 10:if i % 2 0:print(i)死循环,没有输出结果 i1 while i < 10:if i % 2 0:print(i)i l死循环,没有输出结果 i 1 while i< 10…...
代码随想录算法训练营--打卡day3
复习:标注感叹号的需要在电脑上重新做几遍 一.两两交换链表中的节点!! 1.题目链接 24. 两两交换链表中的节点 - 力扣(LeetCode) 2.思路 画图 3.代码 class Solution {public ListNode swapPairs(ListNode head) …...
ubuntu 安装mysql
在 Ubuntu 系统中安装 MySQL 的步骤如下: 步骤 1:更新软件包列表 sudo apt update步骤 2:安装 MySQL 服务器 sudo apt install mysql-server -yUbuntu 22.04/20.04 默认安装 MySQL 8.0,早期版本可能默认使用 MariaDB。 如果需要…...
用Python实现资本资产定价模型(CAPM)
使用 Python 计算资本资产定价模型(CAPM)并获取贝塔系数(β)。 步骤 1:导入必要的库 import pandas as pd import yfinance as yf import statsmodels.api as sm import matplotlib.pyplot as plt 步骤 2࿱…...
Conda配置Python环境
1. 安装 Conda 选择发行版: Anaconda:适合需要预装大量科学计算包的用户(体积较大)。 Miniconda:轻量版,仅包含 Conda 和 Python(推荐自行安装所需包)。 验证安装: co…...
Redisson延迟队列实战:分布式系统中的“时间管理者“
目录 引言:延迟队列的魅力与应用 什么是Redisson延迟队列? 技术原理与工作机制 应用场景 环境准备:搭建基础 Maven依赖配置 Redisson客户端配置 延迟队列实现:核心代码 工作原理深度解析 数据模型与存储结构 元素流转过…...
国产化适配 - YashanDB、达梦数据库与MySQL 的兼容性及技术选型对比分析
根据知识库信息,以下是 YashanDB、达梦数据库与MySQL 的兼容性及技术选型对比分析: 1. YashanDB 与 MySQL 兼容性 协议与语法兼容 : YashanDB 100%兼容 MySQL 5.7协议 的常用命令(如 SELECT、INSERT),但…...
从0开始——在PlatformIO下开展STM32单片机的HAL库函数编程指南
目录 前言 编写时钟初始化 实现Systicks_Handler,完成HAL库的时基更新 编写驱动测试 前言 笔者最开始的尝试是在2025年的寒假,准备向PlatformIO迁移HAL库,注意,截止到目前,PlatformIO对HAL库的支持已经非常完善了。…...
Python小练习系列 Vol.9:杨辉三角生成(数组构建 + 数学组合)
🧠 Python小练习系列 Vol.9:杨辉三角生成(数组构建 数学组合) 🔺 本期我们带来一道简洁却优雅的经典练习 —— 生成杨辉三角,是训练数组操作与组合思想的绝佳题目! 🧩 一、题目描述…...
Webview详解(下)
第三阶段:性能优化 加载速度优化 缓存策略 缓存策略可以显著减少网络请求,提升页面加载速度。常用的缓存策略包括 HTTP 缓存和本地资源预加载。 1. HTTP 缓存 HTTP 缓存利用 HTTP 协议中的缓存机制(如 Cache-Control、ETag 等࿰…...
scss基础用法
SCSS(Sassy CSS)是Sass的增强版本,作为CSS的预处理器,它提供了多种功能来提高代码的可维护性和效率。以下是SCSS的基础用法: 变量(Variables) 用于存储常用的值,如颜色、字体大小等。…...
知能行每日综测
题目1 自己的做法 答案 题目2 自己的 答案 题目3 注意:这道做错了,你们可以看看我哪里错了 题目4 我的 答案 题目5 没思路,不会做 已更改 题目6 答案 第七题 我的 不会 现在补综测最后一个...
c++ vs和g++下的string结构
话不多说进入正题.注:下述结构是在32位平台下进行验证,32位平台下指针占4个字节. vs下string的结构 string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义 string中字符串的存储空间:(联合体的…...
海量数据处理
1.海量数据处理问题 给两个文件,分别有100亿个query,只有1G内存,如何找到两个文件交集? 解决方案一: 可以先用布隆过滤器,一个文件的query放进布隆过滤器,另一个文件依次查找,在的…...
洛谷题单1-P5706 【深基2.例8】再分肥宅水-python-流程图重构
题目描述 现在有 t t t 毫升肥宅快乐水,要均分给 n n n 名同学。每名同学需要 2 2 2 个杯子。现在想知道每名同学可以获得多少毫升饮料(严格精确到小数点后 3 3 3 位),以及一共需要多少个杯子。 输入格式 输入一个实数 t …...
【HarmonyOS 5】初学者如何高效的学习鸿蒙?
【HarmonyOS 5】初学者如何高效的学习鸿蒙? 一、前言 在全球科技格局风云变幻的当下,谷歌安卓系统的管控逐步收紧,加之国际形势愈发复杂,打造中国人自主的操作系统,已成为时代发展的必然要求,这不仅是突破…...
Java NIO之FileChannel 详解
关键点说明 文件打开选项: StandardOpenOption.CREATE - 文件不存在时创建 StandardOpenOption.READ/WRITE - 读写权限 StandardOpenOption.APPEND - 追加模式 StandardOpenOption.TRUNCATE_EXISTING - 清空已存在文件 缓冲区操作: ByteBuffer.wrap…...
数据可视化(matplotlib)-------图表样式美化
目录 一、图表样式概述 (一)、默认图表样式 (二)、图表样式修改 1、局部修改 2、全局修改 二、使用颜色 (一)、使用基础颜色 1、单词缩写或单词表示的颜色 2、十六进制/HTML模式表示的颜色 3、RGB…...
Go 语言中,关于客户端初始化的最佳实践
在 Go 语言中,关于客户端初始化的最佳实践确实需要注意以下几点: 全局单例模式是推荐做法,尤其对于需要保持长连接或需要复用资源的客户端(如数据库、Redis、HTTP 客户端等)并发安全是必须保证的,需要确保…...
MyBatis的第一天笔记
1. MyBatis 概述 1.1 什么是框架 框架是对通用代码的封装,提前写好了一堆接口和类,可以直接引入使用框架一般以jar包形式存在Java常用框架:SSM三大框架(Spring SpringMVC MyBatis)、SpringBoot、SpringCloud等 1.…...
区块链赋能,为木材货场 “智” 造未来
区块链赋能,为木材货场 “智” 造未来 在当今数字化浪潮席卷的时代,软件开发公司不断探索创新,为各行业带来高效、智能的解决方案。今天,让我们聚焦于一家软件开发公司的杰出成果 —— 区块链木材货场服务平台,深入了…...
IvorySQL:兼容Oracle数据库的开源PostgreSQL
今天给大家介绍一款基于 PostgreSQL 开发、兼容 Oracle 数据库的国产开源关系型数据库管理系统:IvorySQL。 IvorySQL 由商瀚高软件提供支持,主要的功能特性包括: 完全兼容 PostgreSQL:IvorySQL 基于 PostgreSQL 内核开发…...
Python 序列构成的数组(切片)
切片 在 Python 里,像列表(list)、元组(tuple)和字符串(str)这类 序列类型都支持切片操作,但是实际上切片操作比人们所想象的要强大 很多。 这一节主要讨论的是这些高级切片形式的…...
Pre-flash和Main flash
在相机拍照过程中,Pre-flash(预闪光) 和 Main flash(主闪光) 是常见的两种闪光灯使用模式,通常用于提高低光环境下的拍摄质量,尤其在自动曝光(AE)和自动对焦(…...
【区块链安全 | 第十篇】智能合约概述
部分内容与前文互补。 文章目录 一个简单的智能合约子货币(Subcurrency)示例区块链基础交易区块预编译合约 一个简单的智能合约 我们从一个基础示例开始,该示例用于设置变量的值,并允许其他合约访问它。 // SPDX-License-Identi…...
判断质数及其优化方法
判断质数(素数)及其优化方法 质数是指 大于1的自然数,且 只有1和它本身两个正约数。以下是几种判断方法及其优化策略。 目录 基础方法(试除法)优化1:仅检查到√n优化2:跳过偶数优化3ÿ…...
【源码阅读/Vue Flask前后端】简历数据查询功能
目录 一、Flask后端部分modelServiceroute 二、Vue前端部分index.js main.vue功能界面templatescriptstyle 一般就是三个层面,model层面用来建立数据库的字段,service用来对model进行操作,写一些数据库操作的代码,route就是具体的…...
R语言对偏态换数据进行转换(对数、平方根、立方根)
我们进行研究的时候经常会遇见偏态数据,数据转换是统计分析和数据预处理中的一项基本技术。使用 R 时,了解如何正确转换数据有助于满足统计假设、标准化分布并提高分析的准确性。在 R 中实现和可视化最常见的数据转换:对数、平方根和立方根转…...
链表(C++)
这是本人第二次学习链表,第一次学习链表是在大一上的C语言课上,首次接触,感到有些难;第二次是在大一下学习数据结构时(就是这次),使用C再次理解链表。同时,这也是开启数据结构学习写…...
算法-前缀和与差分
一、前缀和(Prefix Sum) 1. 核心思想 前缀和是一种预处理数组的方法,通过预先计算并存储数组的前缀和,使得后续的区间和查询可以在**O(1)**时间内完成。 2. 定义 给定数组 nums,前缀和数组 prefixSum 的每个元素 p…...
网关接口超时?用Java实现接口快速返回,后台继续执行的方法
网关接口超时?用Java实现接口快速返回,后台继续执行的方法 在开发过程中,我们经常会遇到网关接口由于超时限制而导致请求失败的情况。然而,有些接口本身就需要较长时间来执行任务,这时我们不能简单地增加超时时间&…...
HTTP---基础知识
天天开心!!! 文章目录 一、HTTP基本概念1. 什么是HTTP,又有什么用?2. 一次HTTP请求的过程3.HTTP的协议头4.POST和GET的区别5. HTTP状态码6.HTTP的优缺点 二、HTTP的版本演进1.各个版本的应用场景2、注意要点 三、HTTP与…...
python基础学习三(元组及字符串的使用)
文章目录 元组什么是元组元组的创建方式为什么要将元组设计成不可变序列元组的遍历集合集合的相关操作集合操作集合的数学操作集合生成式列表,字典,元组,集合总结 字符串字符串的驻留机制判断字符串的操作方法字符串的比较操作字符串的切片操…...
c#winform,倒鸭子字幕效果,typemonkey字幕效果,抖音瀑布流字幕效果
不废话 直接上效果图 C# winform 开发抖音的瀑布流字幕。 也是typemonkey插件字幕效果 或者咱再网上常说的倒鸭子字幕效果 主要功能 1,软件可以自定义添加字幕内容 2,软件可以添加字幕显示的时间区间 3,可以自定义字幕颜色,可以随…...
1、C51单片机(STC8G2K64S4)串口实验
一、串口1接线图 1、下面是单片机外接电路图,P30,P31分别用于RXD和TXD功能引脚 2、我们来查看单片机手册 串口1需要设置的寄存器 串口1的功能脚配置选择位,看电路图选择的是P3.0,P3.1。 3、串口1:SCON控制寄存器 设置为0x50:0101 0000。&a…...
ue材质学习感想总结笔记
2025 - 3 - 27 1.1 加法 对TexCoord上的每一个像素加上一个值,如果加上0.1,0.1, 那么左上角原来0,0的位置变成了0.1,0.1 右上角就变成了1.1,1.1,那么原来0,0的位置就去到了左上角左上边,所以图像往左上偏移。 总而言…...