C++实现消息队列核心模块:路由引擎与队列管理器设计
1. 项目概述与核心思路最近在社区里看到不少朋友对消息队列的实现原理感兴趣尤其是用C这种贴近系统底层的语言来“造轮子”。我自己也一直觉得光会用RabbitMQ、Kafka这些成熟中间件还不够亲手实现一个简化版才能真正吃透消息队列里那些核心的设计思想。所以我决定动手用C仿照RabbitMQ的核心模型实现一个轻量级的消息队列服务端。这不是一个生产级的轮子而是一个深入理解“队列”、“交换器”、“绑定”、“持久化”这些概念的教学级项目。如果你正在学习网络编程、并发模型或者分布式系统基础或者想挑战一下C工程能力这个系列应该能给你带来不少启发。我们最终要实现的服务端核心功能包括支持类似AMQP的Exchange交换器和Queue队列模型实现Direct、Fanout、Topic几种经典的路由模式能够处理客户端的连接、声明、发布和订阅等操作内部要有高效的线程模型来处理并发请求并考虑消息的持久化机制。整个项目会从网络层、协议解析开始逐步构建核心的数据结构和路由逻辑。本篇是这个系列的第四部分我们将聚焦于服务端最核心的模块实现也就是消息路由引擎和队列管理器的构建。这是整个消息队列的“大脑”负责将生产者发来的消息根据既定的规则准确地投递到一个或多个消费者队列中。2. 核心架构设计与模块划分在动手写代码之前我们必须把架构想清楚。一个消息队列服务端的核心职责可以抽象为三件事连接管理、协议处理、消息路由。我们仿RabbitMQ所以架构上也会借鉴它的核心概念。2.1 整体架构视图我们的服务端程序大体上会采用 Reactor 网络模型配合线程池来处理高并发。一个主线程或少数几个负责监听端口和接受新连接Acceptor然后将建立好的连接Connection分发给一组工作线程Worker Thread Pool。每个工作线程运行一个事件循环Event Loop处理其负责的连接上的数据读写事件。这是网络层的常见模式可以使用epoll(Linux) 或IOCP(Windows) 实现但为了简化我们初期可能先用一个简单的线程池配合阻塞IO或select来演示原理。当网络层收到一个完整的数据包并解析后就生成了一个“命令”或“请求”。我们的核心模块就要处理这些请求。架构上核心模块可以划分为以下几个部分虚拟主机VHost与权限管理仿AMQP支持多个逻辑上隔离的虚拟主机。每个VHost有自己的交换器、队列和绑定关系。交换器管理器Exchange Manager负责所有交换器Exchange的生命周期管理。交换器是消息路由的起点有不同的类型Direct, Fanout, Topic。队列管理器Queue Manager负责所有队列Queue的生命周期管理。队列是消息的最终存储和消费点需要维护消息链表、消费者列表等。绑定关系表Binding Table这是路由规则的核心存储。它记录了交换器与队列之间的关联关系Binding对于Topic类型还存储了用于模式匹配的路由键Routing Key模式。消息路由引擎Routing Engine这是最核心的部件。当一个发布Publish请求到来时路由引擎需要根据消息指定的交换器、路由键查询绑定关系表找到所有匹配的队列并将消息投递过去。持久化管理器Persistence Manager可选模块负责将消息、队列元数据等写入磁盘保证服务重启后不丢失。本篇我们将重点实现交换器管理器、队列管理器、绑定关系表和消息路由引擎。虚拟主机和持久化会作为扩展点稍后讨论。2.2 关键数据结构设计设计数据结构是C项目的乐趣所在。我们需要选择既能清晰表达业务概念又能保证性能的容器。交换器Exchange至少需要名字、类型枚举、以及一个标志位表示是否持久化。enum class ExchangeType { DIRECT, FANOUT, TOPIC, HEADERS }; // 我们先实现前三种 struct Exchange { std::string name; ExchangeType type; bool durable{false}; // 其他属性如 auto_delete 等 };队列Queue比交换器复杂它需要存储消息。struct Queue { std::string name; bool durable{false}; bool exclusive{false}; bool auto_delete{false}; // 消息存储。简单起见可以用 std::dequestd::string。 // 但实际需要考虑消息对象包含属性、体、投递状态等。 std::dequeMessage messages; std::mutex queue_mutex; // 保护队列内部状态的互斥锁 // 消费者列表。记录哪些连接/通道正在消费此队列。 std::vectorConsumer consumers; };绑定Binding连接交换器和队列的纽带。对于Direct和Fanout路由键routing_key是精确匹配的字符串对于Topic路由键是包含通配符的模式。struct Binding { std::string exchange_name; std::string queue_name; std::string routing_key; // 对于Fanout这个字段可能为空或忽略 };消息Message这是流动的数据单元。struct Message { std::string body; std::string routing_key; std::mapstd::string, std::string headers; // 用于Headers交换器或应用头 // 投递属性是否持久化、优先级、时间戳等 bool persistent{false}; // 用于实现确认机制的消息ID uint64_t delivery_tag{0}; };有了这些基础结构我们就可以开始搭建管理它们的“管理器”了。3. 核心模块实现详解接下来我们进入具体的代码实现环节。我会先给出类的大致框架然后解释关键方法的实现逻辑和注意事项。3.1 交换器管理器ExchangeManager实现交换器管理器相对简单主要是一个注册表。它的核心是一个字典将交换器名字映射到Exchange对象。class ExchangeManager { public: using ExchangeMap std::unordered_mapstd::string, Exchange; // 声明一个交换器 bool declareExchange(const std::string name, ExchangeType type, bool durable) { std::lock_guardstd::mutex lock(mutex_); if (exchanges_.find(name) ! exchanges_.end()) { // 已存在AMQP规范要求如果参数相同则成功不同则失败。这里简单处理为失败。 // 实际应比较现有参数这里省略。 return false; } exchanges_[name] Exchange{name, type, durable}; return true; } // 删除一个交换器需要检查是否有绑定 bool deleteExchange(const std::string name, bool if_unused) { std::lock_guardstd::mutex lock(mutex_); auto it exchanges_.find(name); if (it exchanges_.end()) { return false; // 不存在 } if (if_unused) { // 需要查询绑定关系表检查是否有队列绑定到此交换器 // 这里假设我们有一个 binding_table_ 的引用或方法 // if (bindingTable_.hasBindingsForExchange(name)) return false; } exchanges_.erase(it); // 同时需要从绑定关系表中清除所有关联此交换器的绑定 // bindingTable_.removeBindingsForExchange(name); return true; } // 根据名字获取交换器只读 std::optionalExchange getExchange(const std::string name) const { std::lock_guardstd::mutex lock(mutex_); auto it exchanges_.find(name); if (it ! exchanges_.end()) { return it-second; } return std::nullopt; } private: mutable std::mutex mutex_; ExchangeMap exchanges_; };注意这里的锁mutex_是一个粗粒度锁保护整个exchanges_映射。在极高并发场景下这可能成为瓶颈。生产环境可能会考虑使用读写锁std::shared_mutexC17或更细粒度的数据结构比如并发哈希表。但对于我们的学习项目std::mutex足够清晰。3.2 队列管理器QueueManager实现队列管理器是状态最重、并发竞争最激烈的模块。它不仅要管理队列元数据还要处理消息的入队、出队以及消费者管理。class QueueManager { public: using QueuePtr std::shared_ptrQueue; // 声明队列 bool declareQueue(const std::string name, bool durable, bool exclusive, bool auto_delete) { std::lock_guardstd::mutex lock(queues_mutex_); if (queues_.find(name) ! queues_.end()) { // 同交换器存在参数检查问题简化处理 return false; } auto queue std::make_sharedQueue(); queue-name name; queue-durable durable; queue-exclusive exclusive; queue-auto_delete auto_delete; queues_[name] queue; return true; } // 绑定队列到交换器这个操作通常由绑定关系表或路由引擎调用 bool bindQueue(const std::string queue_name, const std::string exchange_name, const std::string routing_key) { auto queue getQueue(queue_name); if (!queue) return false; // 绑定逻辑的核心在 BindingTable这里可能只是通知队列有新的绑定来源。 // 我们暂时只做简单检查实际绑定记录在 BindingTable 中。 return true; } // 消息入队 - 这是核心中的核心 bool publishToQueue(QueuePtr queue, Message message) { if (!queue) return false; { std::lock_guardstd::mutex lock(queue-queue_mutex); queue-messages.push_back(std::move(message)); } // 消息入队后需要通知可能正在等待的消费者 notifyConsumers(queue); return true; } // 消费者从队列获取消息Basic.Get 或 Consumer 推送 std::optionalMessage consumeFromQueue(QueuePtr queue, Consumer consumer) { if (!queue) return std::nullopt; std::lock_guardstd::mutex lock(queue-queue_mutex); if (queue-messages.empty()) { return std::nullopt; } auto msg std::move(queue-messages.front()); queue-messages.pop_front(); // 记录投递信息用于后续的确认ACK/NACK // consumer.recordDelivery(msg.delivery_tag); return msg; } private: mutable std::mutex queues_mutex_; // 保护 queues_ 映射表 std::unordered_mapstd::string, QueuePtr queues_; // 获取队列指针内部加锁保护映射表 QueuePtr getQueue(const std::string name) { std::lock_guardstd::mutex lock(queues_mutex_); auto it queues_.find(name); if (it ! queues_.end()) { return it-second; } return nullptr; } // 通知消费者有新消息 void notifyConsumers(QueuePtr queue) { // 这里是一个简化实现。实际需要遍历 queue-consumers // 并通过网络连接向每个消费者推送消息如果处于推送模式。 // 或者如果消费者是拉取模式则可能只是设置一个条件变量唤醒等待的线程。 // 例如 for (auto consumer : queue-consumers) { consumer-notifyNewMessage(); } } };实操心得publishToQueue和consumeFromQueue中的锁queue-queue_mutex是队列级别的锁这与保护queues_映射的queues_mutex_是分开的。这种设计减少了锁的竞争范围。多个线程可以同时向不同的队列发布消息互不干扰。这是实现高性能消息队列的关键点之一。3.3 绑定关系表BindingTable与路由引擎RoutingEngine实现这是整个系统最精巧的部分。绑定关系表存储了路由规则而路由引擎则利用这些规则执行路由决策。我们将它们放在一个类里因为关系紧密。class BindingTable { public: using BindingList std::vectorBinding; using BindingMap std::unordered_mapstd::string, BindingList; // key: exchange_name // 添加一个绑定 bool addBinding(const Binding binding) { std::lock_guardstd::mutex lock(mutex_); // 这里应该检查 exchange 和 queue 是否存在依赖于 ExchangeManager 和 QueueManager bindings_[binding.exchange_name].push_back(binding); return true; } // 路由消息根据交换器名、路由键和交换器类型找到所有应该接收此消息的队列名 std::vectorstd::string routeMessage(const std::string exchange_name, const std::string routing_key, ExchangeType exchange_type) { std::lock_guardstd::mutex lock(mutex_); std::vectorstd::string target_queues; auto it bindings_.find(exchange_name); if (it bindings_.end()) { // 该交换器没有绑定任何队列 return target_queues; } const auto bindings_for_exchange it-second; switch (exchange_type) { case ExchangeType::FANOUT: { // Fanout忽略 routing_key所有绑定的队列都接收 for (const auto binding : bindings_for_exchange) { target_queues.push_back(binding.queue_name); } break; } case ExchangeType::DIRECT: { // Direct精确匹配 routing_key for (const auto binding : bindings_for_exchange) { if (binding.routing_key routing_key) { target_queues.push_back(binding.queue_name); } } break; } case ExchangeType::TOPIC: { // Topic模式匹配。例如 binding.routing_key*.stock.# message.routing_keyusd.stock.nyse for (const auto binding : bindings_for_exchange) { if (topicMatch(binding.routing_key, routing_key)) { target_queues.push_back(binding.queue_name); } } break; } default: break; } return target_queues; } private: mutable std::mutex mutex_; BindingMap bindings_; // Topic 模式匹配函数 bool topicMatch(const std::string pattern, const std::string routing_key) { // 简化实现支持 * (匹配一个单词) 和 # (匹配零个或多个单词) // 单词分隔符是 . // 这是一个经典的递归或双指针匹配算法此处给出一个简单示例 size_t p 0, r 0; size_t p_len pattern.length(), r_len routing_key.length(); size_t p_star std::string::npos, r_star 0; // 用于回溯 while (r r_len) { if (p p_len (pattern[p] routing_key[r] || pattern[p] *)) { // 字符匹配或遇到单级通配符‘*’匹配一个字符实际上是一个单词这里简化 // 对于‘*’我们需要匹配直到下一个‘.’或结尾。 if (pattern[p] *) { while (r r_len routing_key[r] ! .) r; while (p p_len pattern[p] ! .) p; continue; } p; r; } else if (p p_len pattern[p] #) { // 多级通配符‘#’匹配零个或多个单词 p_star p; r_star r; p; // 跳过‘#’ } else if (p_star ! std::string::npos) { // 当前不匹配但有‘#’可以回溯 p p_star 1; r r_star; } else { return false; } } // 处理 pattern 末尾的‘#’或‘*’ while (p p_len pattern[p] #) p; while (p p_len pattern[p] *) { // ‘*’必须匹配一个单词但r已到末尾所以不匹配 // 实际上如果pattern是“queue.*”routing_key是“queue”是不匹配的。 // 这里需要更复杂的逻辑以下为示意。 return false; } return p p_len; } };路由引擎可以封装一下提供一个更简洁的接口给上层网络层调用class RoutingEngine { public: RoutingEngine(std::shared_ptrExchangeManager ex_mgr, std::shared_ptrQueueManager q_mgr, std::shared_ptrBindingTable binding_table) : exchange_manager_(ex_mgr), queue_manager_(q_mgr), binding_table_(binding_table) {} // 处理发布消息的核心入口 bool route(const std::string exchange_name, const std::string routing_key, Message message) { // 1. 查找交换器 auto ex_opt exchange_manager_-getExchange(exchange_name); if (!ex_opt) { // 交换器不存在根据AMQP消息会被静默丢弃或返回错误 return false; } const Exchange exchange *ex_opt; // 2. 查找目标队列 auto target_queue_names binding_table_-routeMessage(exchange_name, routing_key, exchange.type); // 3. 将消息投递到每一个目标队列 bool all_success true; for (const auto qname : target_queue_names) { auto queue queue_manager_-getQueue(qname); // QueueManager 需要提供此方法 if (queue) { bool ok queue_manager_-publishToQueue(queue, message); // 注意这里message被复制了需要优化。 if (!ok) all_success false; } else { // 队列不存在记录错误 all_success false; } } // 4. 如果交换器类型是Fanout/Direct/Topic但没有绑定任何队列消息也会被丢弃。 return all_success; } private: std::shared_ptrExchangeManager exchange_manager_; std::shared_ptrQueueManager queue_manager_; std::shared_ptrBindingTable binding_table_; };踩坑提醒上面的route函数有一个性能问题message被依次投递到多个队列时我们进行了多次拷贝。对于大消息体这是不可接受的。优化方法可以是使用std::shared_ptrconst Message或者自定义引用计数的消息体让所有队列共享同一份消息数据直到所有消费者都确认消费后才释放。这是真实消息队列如RabbitMQ的常见优化。4. 线程安全与并发模型考量我们的核心模块Manager和Table都使用了std::mutex来保护内部数据结构。这是正确的第一步但我们需要审视整个数据流中的锁竞争。锁的粒度我们为ExchangeManager和BindingTable使用了单个互斥锁保护整个哈希表。在交换器和绑定声明/删除不频繁的场景下这可以接受。QueueManager有两层锁保护队列映射的锁和保护单个队列消息链表的锁。这很好。路由热点如果所有消息都发布到同一个热门交换器那么BindingTable::routeMessage里的锁会成为瓶颈。可以考虑使用读写锁std::shared_mutex因为路由操作读远多于绑定变更操作写。消费者通知notifyConsumers目前是空实现。在实际实现中这里可能涉及跨线程通信。例如消费者工作线程可能在条件变量上等待当消息入队后需要notify_one或notify_all来唤醒它们。这要求Queue结构里包含std::condition_variable并且等待和通知的逻辑需要仔细设计避免丢失通知或虚假唤醒。死锁风险如果一个操作需要同时获取多个管理器的锁例如删除一个带有绑定的交换器必须定义严格的锁获取顺序例如总是先锁ExchangeManager再锁BindingTable最后锁QueueManager或者使用std::scoped_lock一次性获取多个锁来避免死锁。一个更高级的模型是无锁队列。对于单个Queue内部的messagesstd::deque我们可以将其替换为无锁链表。但这会大大增加实现复杂度对于学习项目基于锁的队列是更稳妥的选择。5. 与网络层的整合与协议处理核心模块是独立的它需要被网络层驱动。假设我们的网络层解析出了一个PublishCommand它包含exchange_name,routing_key, 和message_body。网络层的工作线程可以这样调用核心模块// 在网络层事件处理线程中 void onPublishCommand(const PublishCommand cmd) { Message msg; msg.body cmd.body; msg.routing_key cmd.routing_key; msg.persistent cmd.persistent; // 从命令中获取 bool routed routing_engine_-route(cmd.exchange_name, cmd.routing_key, std::move(msg)); // 根据 routed 结果向客户端发送确认或错误响应 if (routed) { sendBasicAck(channel, delivery_tag); } else { // 可能交换器不存在发送 Channel.Close 等 } }这里的关键是路由引擎的route方法可能会阻塞因为内部有锁且publishToQueue可能等待消费者通知逻辑。这意味着网络IO线程可能会被阻塞影响整体吞吐。因此更好的架构是将route操作投递到一个专门的后台任务队列中由另一组工作线程来执行网络IO线程迅速返回去处理其他请求。这就是典型的生产者-消费者模式在网络服务中的应用。6. 扩展思考与待实现功能目前我们实现了一个最核心的、内存中的、非持久化的消息路由骨架。要成为一个更完整的“仿RabbitMQ”项目还有很长的路要走虚拟主机VHost将所有的 Manager 和 Table 都放在一个VirtualHost类中服务端维护一个VHost的映射。连接建立时需要指定或默认一个 VHost。消息持久化这是个大话题。需要将持久化的消息和队列元数据写入磁盘例如使用类似SQLite的嵌入式数据库或者直接写文件WAL。Message需要唯一的全局IDQueue需要记录消息的持久化位置。重启后要能恢复状态。确认机制ACK/NACK实现至少一次投递语义。Consumer需要记录已投递未确认的消息Queue需要维护一个“未确认消息”的列表并在消费者断开时重新入队。QoS预取计数限制每个消费者通道上未确认消息的数量实现流量控制。死信交换器DLX消息被拒绝或过期后可以路由到另一个指定的交换器。更高效的Topic匹配我们实现的topicMatch函数非常简陋且可能有bug。生产环境需要使用更高效的算法如将模式编译成状态机或使用Trie树。性能监控与管理接口暴露队列长度、消费者数量、消息吞吐等指标。实现这些功能的过程会让你对 RabbitMQ 官方文档中那些特性的理解深刻十倍。每一个特性背后都是对数据一致性、并发控制和系统设计的挑战。

相关新闻

Agent 工程选型、架构体系、场景落地完整论证

Agent 工程选型、架构体系、场景落地完整论证

流程固定、规则明确场景:优先选择 Workflow,优势是结果可预测、稳定性强、零不确定性。 场景开放、需要柔性决策场景:必须选择 Agent,依靠其动态决策能力适配复杂、不确定的业务场景。 1.2 Agent 专属适配场景 复杂决策类&#xf…

2026/10/13 14:21:24 阅读更多 →
AI如何降低学术研究计算复杂度:从算法优化到科学洞察

AI如何降低学术研究计算复杂度:从算法优化到科学洞察

这次我们来看AI如何改变学术研究的范式——从过度关注计算复杂度回归到真正的科学洞察。传统学术研究中,计算复杂度往往成为瓶颈,研究者需要花费大量时间优化算法效率、处理大规模数据计算,而AI技术的出现正在让这一状况发生根本性转变。AI大…

2026/10/12 9:13:57 阅读更多 →
解决MRTK在Oculus Quest 2上输入框键盘不弹出的配置指南

解决MRTK在Oculus Quest 2上输入框键盘不弹出的配置指南

1. 项目概述:当MRTK在Quest 2上“沉默”时在Unity中为Oculus Quest 2开发混合现实(MR)应用,微软的混合现实工具包(MRTK)几乎是绕不开的利器。它封装了大量交互组件,其中就包括那个看似简单、实则…

2026/10/12 12:52:15 阅读更多 →

最新新闻

Spring Boot 3.4 升级指南:结构化日志、RestClient 与可观测性实战

Spring Boot 3.4 升级指南:结构化日志、RestClient 与可观测性实战

前段时间,我把一个内部项目从 Spring Boot 3.2 升到了 3.4,整体耗时比预期短,但中间踩了几个不翻官方文档根本想不到的坑。这篇文章就基于这次的升级经历,把 Spring Boot 3.4 的“王炸级更新”拆开揉碎讲清楚:哪些新特…

2026/10/14 18:49:04 阅读更多 →
免费AI视频编辑工具全解析:从剪映到达芬奇的实战操作指南

免费AI视频编辑工具全解析:从剪映到达芬奇的实战操作指南

直接一篇干活的文章,不绕弯子。做视频内容这行,天天跟剪辑软件打交道。市面上的专业工具不少,但有个很现实的问题:要么订阅费一年几百上千,要么就是各种来路不明的“绿色版”“破解版”,用起来总觉得不踏实…

2026/10/14 18:49:04 阅读更多 →
Python提取PDF表格到Excel:三引擎协同的稳定方案

Python提取PDF表格到Excel:三引擎协同的稳定方案

简介:本资源是一套面向Python初学者与办公自动化需求者的PDF表格提取实战方案,聚焦解决财务、审计、行政等场景中从年报PDF批量抽取结构化数据并转存Excel的痛点问题。压缩包共14个文件,含3个真实上市公司年报PDF样本、3个对应生成的Excel结果…

2026/10/14 18:49:04 阅读更多 →
Floyd算法详解:从动态规划推导到C++高性能实现与避坑指南

Floyd算法详解:从动态规划推导到C++高性能实现与避坑指南

直接写,不用那些总结开头。Floyd算法,懂的都懂,多源最短路那个,O(n)的经典思想。但讲真,网上关于它的文章,九成是贴个伪代码就跑,要么就是拿个教科书模板让你背。真正到了生产环境,或…

2026/10/14 18:49:04 阅读更多 →
OpenLayers添加Marker完整指南:原理、样式与性能优化

OpenLayers添加Marker完整指南:原理、样式与性能优化

开篇先说个容易踩的坑:很多人把 marker 打成 maker,然后在社区里搜半天搜不到东西。OpenLayers 里的标记点标准写法是 marker,底层其实是一个 Feature,本质就是“在地图某个经纬度坐标上画一个点”。这个点可以是默认的蓝色圆点&a…

2026/10/14 18:49:04 阅读更多 →
Semantic Router 与 vLLM AIBrix 集成部署实战:在 Kubernetes 上搭建可扩展的语义路由推理网关

Semantic Router 与 vLLM AIBrix 集成部署实战:在 Kubernetes 上搭建可扩展的语义路由推理网关

后端API网关模型推理服务AI Agent 【免费下载链接】semantic-router An open, programmable decision layer for models and compute. 项目地址: https://gitcode.com/gh_mirrors/sem/semantic-router 点击查看 免费下载 本指南以 Semantic Router 官方文档中「使用…

2026/10/14 18:48:03 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →