C++ - 仿 RabbitMQ 实现消息队列(2)(Protobuf 和 Muduo 初识)
C++ - 仿 RabbitMQ 实现消息队列(2)(Protobuf 和 Muduo 初识)
- Protobuf
- 1. 序列化/反序列化方法(最核心)
- `_InternalSerialize()`
- `_InternalParse()`
- 2. 内存管理方法
- `SharedCtor()`/`SharedDtor()`
- `InternalSwap()`
- 3. 字段访问方法
- 生成的 getter/setter
- 4. 工具方法
- `ByteSizeLong()`
- `IsInitialized()`
- 关键设计特点:
- 实际使用中最常用的方法:
- muduo
- Reactor模式
- 1. Reactor 是什么?
- 2. Reactor 工作流程
- 3. 与传统阻塞模型的对比
- 4. Reactor 在 Muduo 中的体现
- 5. 为什么用 Reactor?
- 6. 通俗理解案例
- 📌 编译命令建议(请确保 muduo 已正确安装):
- ✅ 测试方法:
我们之前把仿 RabbitMQ 实现消息队列的环境搭建好了,我们今天主要是来使用一下protobuf和Muduo库做一个简单的应用,为之后的项目做准备。
如果之前环境还没有装好的小伙伴可以点击这里:
https://blog.csdn.net/qq_67693066/article/details/147733266
Protobuf
首先,我们查看一下我们有没有装protobuf:
// 定义protobuf的版本
syntax = "proto3";
package chat;//定义消息体
message ChatMessage{string sender = 1; //发送方string text = 2; //文本内容int64 timestamp = 3; //时间戳
}//消息请求
message ChatRequest{enum Type{SEND = 0;QUERY = 1;}Type type = 1;ChatMessage message = 2;
}//消息回应
message ChatResponse{bool success = 1;string info = 2; //回应信息repeated ChatMessage messages = 3;
}
然后执行protoc --cpp_out=. chat.proto,然后就会多出两个文件:
这段代码是使用 Protocol Buffers (protobuf) 定义的一组消息格式,具体来说:
-
syntax = "proto3";
:指定使用 proto3 版本的语法。Proto3 相比于 Proto2 简化了很多特性,并且支持更多语言。 -
package chat;
:定义了一个包声明chat
,这有助于防止不同项目之间的命名冲突。
接下来是三个消息类型定义:
-
message ChatMessage
:定义了一个名为ChatMessage
的消息类型,表示一条聊天消息。它包含了以下字段:string sender = 1;
:发送者的名称。string text = 2;
:消息内容。int64 timestamp = 3;
:消息的时间戳,使用 Unix 时间格式(从 1970 年 1 月 1 日开始计算的秒数)。
-
message ChatRequest
:定义了一个名为ChatRequest
的消息类型,用于请求聊天操作。它包括:enum Type
:一个枚举类型Type
,包含两种可能的值SEND = 0;
和QUERY = 1;
,分别代表发送消息和查询消息的请求类型。Type type = 1;
:请求的类型,使用上述Type
枚举中的一个值。ChatMessage message = 2;
:如果这是一个发送消息的请求,这里会包含一条ChatMessage
。
-
message ChatResponse
:定义了一个名为ChatResponse
的消息类型,用于响应聊天请求。它包括:bool success = 1;
:表示请求是否成功处理。string info = 2;
:提供额外的信息或错误描述。repeated ChatMessage messages = 3;
:一个ChatMessage
类型的列表,用于返回多条消息(例如查询请求的结果)。
这些定义可以被编译成多种编程语言(如 C++, Java, Python等),以便在不同的系统或服务之间传输结构化的数据。
我们看看里面的核心方法有哪些:
从您提供的 Protobuf 生成的 C++ 代码中,最核心的方法可以分为以下几类:
1. 序列化/反序列化方法(最核心)
这些方法是 Protobuf 自动生成的核心功能,用于消息的二进制编码和解码:
_InternalSerialize()
uint8_t* ChatMessage::_InternalSerialize(uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const
- 作用:将消息对象序列化为二进制格式
- 调用链:被
SerializeToArray()
/SerializeToString()
调用 - 重要性:所有网络传输和持久化存储的基础
_InternalParse()
const char* ChatMessage::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx)
- 作用:从二进制数据反序列化为消息对象
- 调用链:被
ParseFromArray()
/ParseFromString()
调用 - 重要性:接收数据时的关键解析方法
2. 内存管理方法
SharedCtor()
/SharedDtor()
void ChatMessage::SharedCtor(::_pb::Arena* arena, bool is_message_owned)
void ChatMessage::SharedDtor()
- 作用:构造/析构时的内存管理
- 特点:处理字符串字段的内存分配和释放
InternalSwap()
void ChatMessage::InternalSwap(ChatMessage* other)
- 作用:高效交换两个对象的内容
- 使用场景:被赋值运算符和移动语义操作调用
3. 字段访问方法
生成的 getter/setter
如:
// string sender = 1;
inline const std::string& sender() const;
inline void set_sender(const std::string& value);
- 作用:提供类型安全的字段访问接口
4. 工具方法
ByteSizeLong()
size_t ChatMessage::ByteSizeLong() const
- 作用:计算消息序列化后的字节大小
- 用途:预先分配缓冲区大小
IsInitialized()
bool ChatMessage::IsInitialized() const
- 作用:检查所有 required 字段是否已设置(在 proto3 中主要用作占位符)
关键设计特点:
- 零拷贝优化:通过
ArenaStringPtr
管理字符串字段,减少内存拷贝 - 延迟解析:Parse 方法支持按需解析字段
- 类型安全:强类型的 getter/setter 方法
- 二进制紧凑:采用 Tag-Length-Value 编码格式
实际使用中最常用的方法:
// 序列化
message.SerializeToString(&output_string);// 反序列化
message.ParseFromString(input_string);// 字段访问
message.set_sender("Alice");
const std::string& sender = message.sender();
这些生成的代码构成了 Protobuf 高效序列化能力的核心,而 Muduo 网络库则会利用这些方法来实现网络消息的收发和处理。
muduo
Muduo 由陈硕大佬开发,是一个基于非阻塞 IO 和事件驱动的 C++高并发 TCP 网络编
程库。 它是一款基于主从 Reactor 模型的网络库,其使用的线程模型是 one loop per
thread, 所谓 one loop per thread 指的是:
- 一个线程只能有一个事件循环(EventLoop), 用于响应计时器和 IO 事件
- 一个文件描述符只能由一个线程进行读写,换句话说就是一个 TCP 连接必须归属
于某个 EventLoop 管理
Reactor模式
Reactor 模型是事件驱动的网络编程模型,它的核心思想是**“当事件发生时,才进行处理”**,而不是阻塞等待。Muduo 网络库正是基于 Reactor 模式设计的。我用最通俗的方式解释它的原理和工作流程:
1. Reactor 是什么?
-
类比:就像餐厅的服务员
- 传统阻塞模型:一个服务员全程服务一桌客人(上菜、倒水、结账都卡住)
- Reactor 模型:一个服务员监听所有桌子的需求(有需求才过去处理)
-
核心三要素:
1. Event Loop(事件循环):不断检查是否有事件发生 2. Demultiplexer(事件分发器):监听哪些socket有事件(如 select/epoll) 3. Event Handler(事件处理器):处理具体事件(如 onMessage)
2. Reactor 工作流程
以 Muduo 的 TCP 服务器为例:
┌───────────────────────────────────────────────────┐│ Event Loop ││ ┌──────────────────────────────┐ ││ │ epoll_wait (监听所有socket) │◀──新连接/数据──┤│ └──────────────┬───────────────┘ ││ 检测到事件│ ││ ┌──────────────▼───────────────┐ ││ │ 根据事件类型分发到对应处理器 │ ││ └──────────────┬───────────────┘ ││ │ ││ ┌──────▼──────┐ ┌─────────────┐ ┌─────────┐ ││ │ 新连接处理器 │ │ 数据处理器 │ │ 错误处理器│ ││ │ (onConnection)│ │ (onMessage) │ │ (onError) │ ││ └─────────────┘ └─────────────┘ └─────────┘ │└───────────────────────────────────────────────────┘
3. 与传统阻塞模型的对比
特性 | Reactor 模型 | 阻塞模型 |
---|---|---|
线程使用 | 单线程处理多连接(IO多路复用) | 每个连接一个线程 |
资源消耗 | 低(1个线程管理成千上万个连接) | 高(线程数=连接数) |
响应方式 | 事件触发(有数据才唤醒) | 阻塞等待(没数据也占着线程) |
典型实现 | Muduo、Nginx、Redis | 早期Java BIO |
4. Reactor 在 Muduo 中的体现
// 1. 创建事件循环(Reactor核心)
EventLoop loop;// 2. 创建服务器并注册事件处理器
TcpServer server(&loop, listenAddr, "EchoServer");
server.setConnectionCallback(onConnection); // 新连接事件处理器
server.setMessageCallback(onMessage); // 数据到达事件处理器// 3. 启动事件循环(开始监听事件)
loop.loop(); // 内部调用 epoll_wait
- 当事件发生时:
epoll_wait
返回活跃的 socket- EventLoop 根据事件类型(新连接/数据到达)调用对应的
Callback
- 你的
onMessage
等函数被触发执行
5. 为什么用 Reactor?
- 高性能:单线程可处理数万并发连接(如 Nginx)
- 低延迟:没有线程切换开销
- 适合场景:
- 短连接服务(HTTP)
- 高并发低延迟(即时通讯)
- 需要精细控制IO的场合(代理服务器)
6. 通俗理解案例
假设你经营一家奶茶店:
- 传统模式:每个顾客配一个店员(线程),店员必须等顾客做完决定才能服务下一个
- Reactor模式:
- 1个前台(EventLoop)监听所有顾客(socket)
- 顾客举手(事件发生)时,前台派空闲店员(Callback)去处理
- 没顾客举手时,前台可以喝茶休息(
epoll_wait
休眠)
我们来看一个简单的例子:
#include <muduo/net/TcpServer.h> // TcpServer 类,用于创建 TCP 服务器
#include <muduo/net/EventLoop.h> // EventLoop 事件循环类,负责 I/O 多路复用和事件处理
#include <muduo/base/Logging.h> // 日志系统,用于输出调试、信息、错误等日志using namespace muduo;
using namespace muduo::net;// 定义消息回调函数:当客户端发送数据时,该函数会被调用
// 参数说明:
// - conn: 表示当前连接对象的智能指针
// - buf: 接收到的数据缓冲区(muduo 的 Buffer 类型)
// - time: 接收数据的时间戳(本例中未使用)
void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp)
{// 将接收的数据转换为字符串std::string msg = buf->retrieveAllAsString(); // 提取全部数据并清空缓冲区// 在终端打印接收到的消息内容printf("[Server] Received: %s\n", msg.c_str());// 将接收到的数据原样返回给客户端conn->send(buf); // send 函数接受的是 Buffer* 类型,所以可以直接传入 buf
}int main()
{// 设置日志输出级别为 DEBUG,这样会显示更多的调试信息// 默认情况下,muduo 可能只输出 INFO 和以上级别的日志Logger::setLogLevel(Logger::DEBUG);// 创建一个 EventLoop 对象,它是整个 muduo 网络模型的核心// 所有的网络事件(如 accept、read、write)都会在这个 loop 中处理EventLoop loop; // 核心事件循环// 创建 TcpServer 实例// 参数说明:// - loop: 使用哪个 EventLoop 来驱动服务器// - InetAddress(8888): 监听本地所有 IP 地址(0.0.0.0)上的 8888 端口// - "SimpleServer": 服务器名称,用于日志输出中的标识TcpServer server(&loop, InetAddress(8888), "SimpleServer");// 设置消息回调函数// 当服务器从客户端读取到数据时,就会调用这个 onMessage 函数server.setMessageCallback(onMessage);// 启动服务器// 这个函数不会立即开始监听端口,而是在下一次进入 EventLoop 后执行初始化操作server.start();// 启动事件循环// 进入主事件循环后,程序将一直运行,等待客户端连接和数据的到来loop.loop();return 0;
}
📌 编译命令建议(请确保 muduo 已正确安装):
g++ -std=c++11 echo_server.cpp -lmuduo_net -lmuduo_base -lpthread -o echo_server
✅ 测试方法:
-
启动服务端:
./echo_server
-
打开另一个终端,使用 telnet 或 nc 测试:
telnet 127.0.0.1 8888
或者:
nc localhost 8888
-
在客户端输入任意文本,例如:
Hello Muduo!
-
你会看到服务端打印类似信息:
[Server] Received: Hello Muduo!
-
同时客户端也会收到同样的回显消息。
这样我们可以利用muduo库编写一个简单的程序,客户端连接到服务端之后,服务端回显客户端发送的数据:
客户端代码:
client.h
#ifndef CHAT_CLIENT_H
#define CHAT_CLIENT_H#include <muduo/net/TcpClient.h>
#include <functional>class ChatClient {
public:using ConnectionCallback = std::function<void()>;ChatClient(muduo::net::EventLoop* loop, const muduo::net::InetAddress& serverAddr);void connect();void sendMessage(const std::string& text);bool isConnected() const;private:void onConnection(const muduo::net::TcpConnectionPtr& conn);void onMessage(const muduo::net::TcpConnectionPtr& conn,muduo::net::Buffer* buf,muduo::Timestamp time);muduo::net::TcpClient client_;muduo::net::TcpConnectionPtr conn_;
};#endif
client.cpp
#include "client.h"
#include <muduo/base/Logging.h>
#include <iostream>using namespace muduo;
using namespace muduo::net;ChatClient::ChatClient(EventLoop* loop, const InetAddress& serverAddr): client_(loop, serverAddr, "ChatClient") {client_.setConnectionCallback(std::bind(&ChatClient::onConnection, this, _1));client_.setMessageCallback(std::bind(&ChatClient::onMessage, this, _1, _2, _3));
}void ChatClient::connect() {client_.connect();
}bool ChatClient::isConnected() const {return conn_ && conn_->connected();
}void ChatClient::sendMessage(const std::string& text) {if (!isConnected()) {std::cout << "Not connected to server" << std::endl;return;}conn_->send(text);
}void ChatClient::onConnection(const TcpConnectionPtr& conn) {if (conn->connected()) {conn_ = conn;LOG_INFO << "Connected to server";} else {conn_.reset();LOG_INFO << "Disconnected from server";}
}void ChatClient::onMessage(const TcpConnectionPtr& conn,Buffer* buf,Timestamp time) {std::string msg(buf->retrieveAllAsString());std::cout << "Server reply: " << msg << std::endl;
}
主函数:
client_main.cpp
#include "../client/client.h"
#include <muduo/net/EventLoop.h>
#include <iostream>
#include <thread>
#include <atomic>std::atomic<bool> g_running(true);void handleInput(ChatClient& client) {std::string line;while (g_running.load() && std::getline(std::cin, line)) {if (line == "quit") {g_running.store(false);break;}client.sendMessage(line);}
}int main(int argc, char* argv[]) {if (argc < 3) {std::cerr << "Usage: " << argv[0] << " <server_ip> <server_port>" << std::endl;return 1;}muduo::net::EventLoop loop;muduo::net::InetAddress serverAddr(argv[1], std::stoi(argv[2]));ChatClient client(&loop, serverAddr);std::thread inputThread(handleInput, std::ref(client));client.connect();loop.loop();g_running.store(false);if (inputThread.joinable()) {inputThread.join();}return 0;
}
服务端代码:
server.h
#ifndef CHAT_SERVER_H
#define CHAT_SERVER_H#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/base/Logging.h>
#include <unordered_set>
#include "chat.pb.h"class ChatServer {
public:ChatServer(muduo::net::EventLoop* loop,const muduo::net::InetAddress& listenAddr);void start();private:void onConnection(const muduo::net::TcpConnectionPtr& conn);void onMessage(const muduo::net::TcpConnectionPtr& conn,muduo::net::Buffer* buf,muduo::Timestamp time);void handleRequest(const muduo::net::TcpConnectionPtr& conn,const chat::ChatRequest& request);muduo::net::TcpServer server_;std::unordered_set<muduo::net::TcpConnectionPtr> connections_;std::vector<chat::ChatMessage> chatHistory_;
};#endif // CHAT_SERVER_H
server.cpp
#include "server.h"
#include "chat.pb.h"
#include <muduo/base/Logging.h>using namespace muduo;
using namespace muduo::net;ChatServer::ChatServer(EventLoop* loop, const InetAddress& listenAddr): server_(loop, listenAddr, "ChatServer") {server_.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));
}void ChatServer::start() {server_.start();
}void ChatServer::onMessage(const TcpConnectionPtr& conn,Buffer* buf,Timestamp time) {// 获取并打印完整消息内容std::string msg = buf->retrieveAllAsString();printf("收到客户端消息[长度:%lu]: %s\n", msg.size(), msg.c_str());// 简单回复确认(包含原始消息)std::string reply = "Server收到消息: " + msg;conn->send(reply);
}
主函数:
server_main.cpp
#include "../server/server.h"
#include <muduo/net/EventLoop.h>int main() {muduo::net::EventLoop loop;muduo::net::InetAddress listenAddr(8888);ChatServer server(&loop, listenAddr);server.start();loop.loop();return 0;
}
效果如下:
相关文章:
C++ - 仿 RabbitMQ 实现消息队列(2)(Protobuf 和 Muduo 初识)
C - 仿 RabbitMQ 实现消息队列(2)(Protobuf 和 Muduo 初识) Protobuf1. 序列化/反序列化方法(最核心)_InternalSerialize()_InternalParse() 2. 内存管理方法SharedCtor()/SharedDtor()InternalSwap() 3. 字…...
FTP与NFS服务实战:从配置到应用
一、FTP服务进阶:客户端工具与访问控制 1. FTP客户端工具对比 在Linux中,ftp和lftp是常用的FTP客户端工具,功能各有侧重: 工具特点适用场景ftp基础命令交互,需手动输入用户名/密码简单文件传输lftp支持多协议、批量…...
高考AI试题查询系统
高考AI试题查询系统 gitee:https://gitee.com/ltyyyds26/GaoKao_AI 数据 来源:OpenLMLab/GAOKAO-Bench: GAOKAO-Bench is an evaluation framework that utilizes GAOKAO questions as a dataset to evaluate large language models. (github.com) 数…...
记录算法笔记(2025.5.17)验证二叉搜索树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下: 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1: 输入&…...
DataX:一个开源的离线数据同步工具
DataX 是一个异构数据源离线同步(ETL)工具,实现了包括关系型数据库(MySQL、Oracle 等)、HDFS、Hive、ODPS、HBase、FTP 等各种异构数据源之间稳定高效的数据同步功能。它也是阿里云 DataWorks 数据集成功能的开源版本。 为了解决异构数据源同…...
剑指offer第一周
目录 二维数组中的查找 旋转数组的最小数字 调整数组顺序使奇数位于偶数前面 数组中出现次数超过一半的数字 替换空格 从尾到头打印链表 重建二叉树 矩形覆盖 链表中倒数最后k个结点 二进制中1的个数 合并两个排序的链表 树的子结构 二叉树的镜像 二…...
素数筛(欧拉筛算法)
#include<bits/stdc.h> using namespace std; #define maxn 100000 int vis[maxn]; int prime[maxn]; //欧拉筛函数 int Euler_sieve(int n) { int i,j,k; k0;//保存素数的个数 memset(vis,0,sizeof(int)*maxn);//初始化数组 for(i2;i<n;i) { if(vis[i]0)//i是素数…...
遨游科普:三防平板是什么?有什么功能?
清晨的露珠还挂在帐篷边缘,背包里的三防平板却已开机导航;工地的尘土飞扬中,工程师正通过它查看施工图纸;暴雨倾盆的救援现场,应急队员用它实时回传灾情数据……这些看似科幻的场景,正因三防平板的普及成为…...
CSS 浮动与定位以及定位中z-index的堆叠问题
CSS 浮动与定位以及定位中z-index的堆叠问题 一、浮动布局的特点与应用 1. 浮动核心特性 脱离标准流:浮动元素会脱离文档流。环绕特性:后续内容会环绕浮动元素排列自动换行:多个浮动元素在容器宽度不足时自动换行 .float-box {float: lef…...
在Maven中替换文件内容的插件和方法
在Maven中替换文件内容的插件和方法 Maven提供了几种方式来替换文件内容,以下是常用的插件和方法: 1. maven-replacer-plugin (推荐) 这是专门用于文件内容替换的插件,功能强大且灵活。 基本配置 <plugin><groupId>com.goog…...
C# lock
在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块。这是一种简单的同步机制,用来防止多个线程同时访问共享资源或执行需要独占访问的代码段(临界区),从而…...
OGGMA 21c 微服务 (MySQL) 安装避坑指南
前言 这两天在写 100 天实战课程 的 OGG 微服务课程: 在 Oracle Linux 8.10 上安装 OGGMA 21c MySQL 遇到了一点问题,分享给大家一起避坑! 环境信息 环境信息: 主机版本主机名实例名MySQL 版本IP 地址数据库字符集Goldengate …...
NPN、PNP三极管的应用
由于电路知识实在是难以拿出手,在面试的时候被问到三极管相关问题,相当地尴尬。在网上简要地学习了相关的理论知识,在这里给出自己的理解。更为基础的原理在这里并不提及。我们面向实际应用学习即可。 我们知道常见的三极管总是硅管ÿ…...
Cadence Allegro安装教程及指导
Cadence Allegro 是一款专业的 PCB 设计软件,被广泛应用于电子行业。它功能强大,能够处理复杂的电路板设计任务。下面为你详细介绍 Cadence Allegro 的安装步骤。 一、安装前准备 在安装 Cadence Allegro 之前,需要进行一系列准备工作&…...
阿里通义万相 Wan2.1-VACE:开启视频创作新境界
2025 年 5 月 14 日,阿里巴巴为视频创作领域带来了重磅惊喜 —— 开源通义万相 Wan2.1-VACE。这一模型堪称视频生成与编辑领域的集大成者,凭借其全面且强大的功能,为广大创作者、开发者以及企业用户开辟了全新的视频创作天地。它打破了以往视…...
mAP、AP50、AR50:目标检测中的核心评价指标解析
在目标检测任务中,评价指标是衡量模型性能的核心工具。其中,mAP(mean Average Precision)、AP50(Average Precision at IoU0.5)和AR50(Average Recall at IoU0.5)是最常用的指标。本…...
Linux进程异常退出排查指南
在 Linux 中,如果进程无法正常终止(如 kill 命令无效)或异常退出,可以按照以下步骤排查和解决: 1. 常规终止进程 尝试普通终止(SIGTERM) kill PID # 发送 SIGTERM 信号(…...
深入解析:如何基于开源OpENer开发EtherNet/IP从站服务
一、EtherNet/IP协议概述 EtherNet/IP(Industrial Protocol)是一种基于以太网的工业自动化通信协议,它将CIP(Common Industrial Protocol)封装在标准以太网帧中,通过TCP/IP和UDP/IP实现工业设备间的通信。作为ODVA(Open DeviceNet Vendors Association)组织的核心协议…...
【Linux 学习计划】-- yum
目录 什么是yum Linux的生态讲解 yum相关操作 yum源 yum配置相关问题 结语 什么是yum 我们的手机上都有手机自带的软件商城,我们下载软件都可以在上面搜索,安装,下载 而我们的yum就是这么一个东西,他其实就是Linux下的安装…...
Qt 强大的窗口停靠浮动
1、左边: 示例代码: CDockManager::setConfigFlags(CDockManager::DefaultOpaqueConfig); CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true); dockManager new CDockManager(this); // Disabling the Internal Style S…...
Flink 数据传输机制
在 Apache Flink 中,数据传输(Data Transmission)机制 是其分布式流处理能力的核心之一。Flink 通过高效的内部数据交换、网络通信和序列化机制,确保任务之间的数据能够高效、可靠地流动。 一、Flink 数据传输的基本流程 Source …...
数据库——SQL约束窗口函数介绍
4.SQL约束介绍 (1)主键约束 A、基本内容 基本内容 p r i m a r y primary primary k e y key key约束唯一表示数据库中的每条记录主键必须包含唯一的值(UNIQUE)主键不能包含NULL值(NOT NULL)每个表都应…...
第8讲、Multi-Head Attention 的核心机制与实现细节
🤔 为什么要有 Multi-Head Attention? 单个 Attention 机制虽然可以捕捉句子中不同词之间的关系,但它只能关注一种角度或模式。 Multi-Head 的作用是: 多个头 多个视角同时观察序列的不同关系。 例如: 一个头可能专…...
【发票提取表格】批量PDF电子发票提取明细保存到Excel表格,批量提取ODF电子发票明细,行程单明细,单据明细保存到表格,使用步骤、详细操作方法和注意事项
在日常办公中,我们常常会面临从大量 PDF 电子发票、ODF 电子发票、行程单及各类单据中提取明细,并整理到 Excel 表格的艰巨任务。手动操作不仅耗时费力,还极易出错。以下为您详细介绍其使用步骤、操作方法、注意事项及应用场景。 一、适用场…...
React中startTransition的使用
// 引入 React 的 Hook API:useState 管理状态、useTransition 处理非紧急更新、useMemo 缓存计算结果 import { useState, useTransition, useMemo } from react;/*** List 组件:* 根据输入的 query 动态渲染一个包含 10000 条数据的列表*/ function Li…...
Reactor (epoll实现基础)
Reactor 是什么? Reactor 网络模型是一种高性能的事件驱动模型,广泛应用于网络编程中。它通过 I/O 多路复用技术,实现了高效的事件处理和系统吞吐量的优化。 核心概念 Reactor 模型_的核心是事件驱动,即当 I/O 事件准备就绪时_…...
php fiber 应用
参考 基于 PHP Fiber(纤程)的游戏开发分析-腾讯云开发者社区-腾讯云PHP 8.1 引入的 Fibers 为游戏开发带来新机遇,能管理渲染、物理计算等任务且不阻塞主线程。它支持并发,提升效率,简单易用,但也有局限&a…...
前端扫盲HTML
文章目录 下载、安装、运行第一个代码(hello world)创建代码文件编辑代码(hello world)HTML常见标签注释标签标题标签段落标签换行标签格式化标签图片标签表格标签列表标签表单标签下拉菜单无语义标签 参考文档 下载、安装、运行第…...
RAG与微调:企业知识库落地的技术选型
从本质上看,RAG是"让模型查阅外部知识",而微调是"让模型学会并内化知识"。这一根本差异决定了它们在不同场景下的适用性。 技术选型的关键依据 场景RAG微调说明模型定制化需求❌✅微调更适合塑造特定风格、口吻和人格特征硬件资源…...
Linux安全篇 --firewalld
一、Firewalld 防火墙概述 1、Firewalld 简介 firewalld 的作用是为包过滤机制提供匹配规则(或称为策略),通过各种不同的规则告诉netfilter 对来自指定源、前往指定目的或具有某些协议特征的数据包采取何种处理方式为了更加方便地组织和管理防火墙,firewalld 提供…...
关于Android Studio for Platform的使用记录
文章目录 简单介绍如何使用配置导入aosp工程配置文件asfp-config.json 简单介绍 Android Studio for Platform是google最新开发,用来阅读aosp源码的工具 详细的资料介绍: https://developer.android.google.cn/studio/platform 将工具下载下来直接点击…...
搜索引擎工作原理|倒排索引|query改写|CTR点击率预估|爬虫
写在前面 使用搜索引擎是我们经常做的事情,搜索引擎的实现原理。 什么是搜索引擎 搜索引擎是一种在线搜索工具,当用户在搜索框输入关键词时,搜索引擎就会将与该关键词相关的内容展示给用户。比较大型的搜索引擎有谷歌,百度&…...
【找工作系列①】【大四毕业】【复习】巩固JavaScript,了解ES6。
文章目录 前言Tasks:复习笔记:JavaScript是什么?JavaScript有什么用或者换句话说 是做什么的?JavaScript由哪几部分组成?BOM?DOM?html文件中script标签放在哪里?🧩 1. **放在 ****<head>**** 中**✅ 优点&…...
Oracle 11.2.0.4 pre PSU Oct18 设置SSL连接
Oracle 11.2.0.4 pre PSU Oct18 设置SSL连接 1 说明2 客户端配置jdk环境3服务器检查oracle数据库补丁4设置ssla 服务器配置walletb 上传测试脚本和配置文件到客户端c 服务器修改数据库侦听和sqlnet.orad 修改客户端的sqlnet.ora和tnsnames.ora的连接符e 修改java代码的数据连接…...
本地部署开源网盘系统 kiftd 并实现外部访问(Linux 版本)
kiftd 是一款专为个人、团队及小型组织设计的开源网盘系统,兼具便捷性、跨平台兼容性与丰富的功能,成为替代传统文件共享工具的理想选择。 本文将详细介绍如何在 Linux 系统本地部署 kiftd 并结合路由侠实现外网访问本地部署的 kiftd 。 第一步&#x…...
ECS/GEM是半导体制造业的标准通信协议中host和equipment的区别是什么,在交互过程中,如何来定位角色谁为host,谁为equipment
文章目录 一、角色定义与核心区别1. Host(主机)2. Equipment(设备)3. Host与Equipment的核心区别 二、交互过程中的角色定位1. 交互方向2. 控制层级3. 交互过程中角色的定位方法3.1. 通信发起方向3.2. 协议功能与状态管理3.3. 物理…...
5000 字总结CSS 中的过渡、动画和变换详解
CSS 中的过渡、动画和变换详解 一、CSS 过渡(Transitions) 1. 基本概念 CSS 过渡是一种平滑改变 CSS 属性值的机制,允许属性值在一定时间内从一个值逐渐变化到另一个值,从而创建流畅的动画效果。过渡只能用于具有中间值的属性&…...
2025年渗透测试面试题总结-安恒[实习]安全工程师(题目+回答)
网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 安恒[实习]安全工程师 一面 1. 自我介绍 2. 前两段实习做了些什么 3. 中等难度的算法题 4. Java的C…...
WebXR教学 09 项目7 使用python从0搭建一个简易个人博客
WebXR教学 09 项目7 使用python从0搭建一个简易个人博客(1) 前期设计规划 功能 呈现个人博客文章 技术选型 HTMLCSSJSPythonFlask 环境准备 VS Code Python3.8 代码实现 包 # 创建虚拟环境(-m 会先将模块所在路径加入 sys.path,更适…...
c++从入门到精通(五)--异常处理,命名空间,多继承与虚继承
异常处理 栈展开过程: 栈展开过程沿着嵌套函数的调用链不断查找,直到找到了与异常匹配的catch子句为止;也可能一直没找到匹配的catch,则退出主函数后查找过程终止。栈展开过程中的对象被自动销毁。 在栈展开的过程中,…...
开源安全大模型Foundation-Sec-8B实操
一、兴奋时刻 此时此刻,晚上22点55分,从今天早上6点左右开始折腾,花费了接近10刀的环境使用费,1天的休息时间,总算是把Foundation-Sec-8B模型跑起来了,中间有两次胜利就在眼前,但却总在远程端口转发环节出问题,让人难受。直到晚上远程Jupyter访问成功那一刻,眉开眼笑,…...
现代优化算法全解析:禁忌搜索算法、模拟退火算法、遗传算法、蚁群优化算法、人工神经网络
现代优化算法全解析:禁忌搜索算法、模拟退火算法、遗传算法、蚁群优化算法、人工神经网络 引言:为什么需要优化算法? 在当今这个数据驱动的时代,优化算法已成为计算机科学、工程设计、人工智能等领域的核心工具。无论是训练神经…...
Docker常见命令解读
上图是对docker常见命令的一个图解,方便大家理解,下面,我将对这些命令做一些解释。 一、镜像生命周期管理 1. 镜像构建(Build) docker build -t my-image . # 根据Dockerfile构建镜像 Dockerfile:…...
为什么 Docker 建议关闭 Swap
在使用 Docker 时,关闭系统 Swap(交换分区) 是一个常见的推荐做法,尤其是在生产环境中。虽然 Docker 不强制要求禁用 Swap,但出于性能、稳定性、可控性和资源管理的目的,通常建议这样做。 为什么 Docker 建…...
TIFS2024 | CRFA | 基于关键区域特征攻击提升对抗样本迁移性
Improving Transferability of Adversarial Samples via Critical Region-Oriented Feature-Level Attack 摘要-Abstract引言-Introduction相关工作-Related Work提出的方法-Proposed Method问题分析-Problem Analysis扰动注意力感知加权-Perturbation Attention-Aware Weighti…...
WPS PPT设置默认文本框
被一个模板折磨了好久,每次输入文本框都是很丑的24号粗体还有行标,非常恶心,我甚至不知道如何描述自己的问题,非常憋屈,后来终于知道怎么修改文本框了。这种软件操作问题甚至不知道如何描述问题本身,非常烦…...
支持selenium的chrome driver更新到136.0.7103.94
最近chrome释放新版本:136.0.7103.94 如果运行selenium自动化测试出现以下问题,是需要升级chromedriver才可以解决的。 selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only su…...
“下一辆车还买小米”
大家好,我是小悟。 就在5月13日,江西上饶德兴街头,一辆紫色小米SU7 Max停在路边,却遭遇了一场堪比灾难片的意外。 一辆满载货物的大货车因手刹故障溜坡,径直撞向SU7,两车从两米高的落差坠落,货…...
opencv4.11生成ArUco标记 ArUco Marker
从opencv4.7开始aruco有了一些变化 以下是opencv4.11生成ArUco标记的小例子 #include <iostream> #include <opencv2/opencv.hpp> #include <opencv2/objdetect/aruco_detector.hpp>int main() {cv::Mat markerImage;cv::aruco::Dictionary dictionary cv…...
从辅助到协作:GitHub Copilot的进化之路
如果说现代程序员的标配工具除了VS Code、Stack Overflow之外,还有谁能入选,那一定是GitHub Copilot。从2021年首次亮相,到如今深度集成进开发者日常流程,这个“AI编程助手”已经不只是写几行自动补全代码的小帮手了,而…...