Hical 性能优化全记录

优化背景 Hical 是我写的 C++20/26 Web 框架,跑 Hello World 压测时起初只有 27K QPS,而同类框架(Cinatra 165K、Drogon 170K)差了将近一个数量级。目标很明确:追平 Cinatra/Drogon 的水平。 整个优化过程分 6 个阶段,不是拍脑袋乱改,每一步都是 perf + 火焰图定位瓶颈 → 想方案 → 写代码 → 跑压测验证 的循环。能看到数字变化才算数。 阶段 1:协程帧削减(v2.5.1-v2.5.2) 发现问题 perf 火焰图第一个大头:14.5% CPU 在 scheduler::wake_one_thread_and_unlock + pthread_cond_signal。 一开始以为是跨线程调度问题,仔细一看不是——是 Boost.Asio scheduler 每次 co_await resume 都要走的内部调度流程太重了。一个 Hello World 请求居然走了 4 个协程帧: 1 2 3 4 5 handleSession: co_await async_read → 帧 1(必需,I/O 等待) co_await router_.dispatch() → 帧 2(Router 本身是协程) co_await handler(req) → 帧 3(同步 handler 被包装成协程,不必要!) co_await async_write → 帧 4(必需,I/O 等待) 帧 1 和 4 是真正的 I/O 等待不可消除,但帧 2 和 3 完全是浪费——一个同步的 return HttpResponse("Hello") 被裹了两层协程。 ...

May 22, 2026 · 9 min · 1794 words

Hical 框架开发心得:七个深刻教训

Hical 框架开发心得:七个深刻教训 引言 Hical 是一个基于 Boost.Asio 的现代 C++20/26 高性能 Web 框架,采用原生 HTTP/WebSocket 网络栈(picohttpparser + 自研 WebSocket),从第一行代码到现在的 45+ 测试文件、3 层内存池、协程化数据库中间件、自研日志系统、OpenAPI 自动生成、WsHub 广播管理器、QPS 从 27K 到 159K 的优化历程,一路走来踩了不少坑,也收获了很多。 这篇文章不讲 API 用法,也不讲架构教程——那些在其他文章里都有。这篇只聊开发过程中的真实体会:哪些决策事后证明是对的,哪些看似优雅的方案差点把自己埋了,以及最终选择背后的取舍逻辑。 目录 Hical 框架开发心得:七个深刻教训 引言 目录 一、C++20 协程是双刃剑 1.1 协程让异步代码变清晰了……吗? 1.2 co_await 后 this 可能已经死了 1.3 io_context 析构时的协程帧:成员声明顺序陷阱 1.4 co_spawn(detached) 的悬空引用陷阱 1.5 异常传播:catch 里不能 co_await 1.6 收获:协程不是银弹 二、PMR 三层内存池——收益大但陷阱多 2.1 为什么要三层 2.2 踩坑:upstream 选错导致跨线程竞争 2.3 踩坑:allocator 忘了传播 2.4 收获:PMR 的收益在高并发场景才显现 三、模板 + Concepts 比虚函数继承更适合网络框架 3.1 GenericConnection 的零成本分流 3.2 NetworkBackend concept:可替换但不多态 3.3 收获:编译期分支 > 运行时分支 四、自研日志系统的价值 4.1 为什么不用 spdlog 4.2 六层架构的设计决策 4.3 两个关键的性能优化 4.4 收获:核心框架值得自研,应用项目直接用 spdlog 五、双轨反射——为未来留后路 5.1 问题:C++26 还没来,但 API 要现在设计 5.2 宏回退层的实现策略 5.3 收获:用宏模拟未来语言特性 六、移除 Boost.Beast——火焰图驱动的依赖清退 6.1 Beast 到底慢在哪 6.2 picohttpparser + 零拷贝请求 6.3 自研 WebSocket 栈的取舍 6.4 收获:数据驱动的依赖决策 七、WsHub 广播管理器——从"能用"到"好用"的 WebSocket 架构 7.1 问题:裸连接管理的困境 7.2 WsHub 的核心设计 7.3 写串行化:被忽视的并发陷阱 7.4 收获:框架应该管理连接生命周期 后记:ThreadSanitizer CI 揪出的隐藏竞态(v2.6) Boost.Asio 对象的跨线程操作 stop() 的并发调用:门卫模式 教训 总结:七条核心原则 一、C++20 协程是双刃剑 1.1 协程让异步代码变清晰了……吗? 在引入协程之前,一个 HTTP 请求的处理链是嵌套回调: ...

May 18, 2026 · 16 min · 3291 words

Hical 生产部署实践:从编译优化到 Kubernetes 容器化

Hical 生产部署实践:从编译优化到容器化 框架开发完了,测试也通过了——然后呢?“本地跑得好好的"和"线上稳定运行"之间,隔着编译优化、进程管理、反向代理、监控告警、容器编排一整套工程实践。这篇文章把 Hical 从开发环境搬到生产环境的完整链路走一遍,每个环节都给出可直接复用的配置模板。 目录 Hical 生产部署实践:从编译优化到容器化 目录 一、编译优化:榨干最后一点性能 1.1 Release 基础参数 1.2 LTO(链接时优化) 1.3 PGO(Profile-Guided Optimization) 1.4 静态链接 vs 动态链接 二、进程管理:别让服务裸奔 2.1 systemd 服务配置 2.2 信号处理与 Graceful Shutdown 2.3 多线程与多 acceptor(SO_REUSEPORT) 三、反向代理:Nginx 挡在前面 3.1 HTTP 反向代理 3.2 WebSocket 代理 3.3 SSL 终止策略 四、监控与可观测性 4.1 Prometheus 指标暴露 4.2 日志接入 ELK / Loki 4.3 健康检查端点 五、容器化部署 5.1 多阶段 Dockerfile 5.2 docker-compose 完整示例 5.3 Kubernetes 部署参考 六、性能调优检查清单 系统级 Hical 应用级 PMR 内存池 数据库连接池 日志系统 调优流程 一、编译优化:榨干最后一点性能 1.1 Release 基础参数 开发阶段用 Debug 方便调试,上线必须切 Release。区别不只是 -O2,还有 assert 消除、NDEBUG 定义(Hical 的 HICAL_LOG_TRACE 宏在 NDEBUG 下编译期完全消除): ...

May 17, 2026 · 15 min · 2992 words

Hical VS2022 快速上手 Demo

Hical VS2022 快速上手 Demo 一个最小化的 Hical HTTP Server 示例,VS2022 编译即可运行,访问即返回含中文的 JSON 响应。 环境要求 项目 版本要求 Visual Studio 2022 17.6+ (需勾选"C++桌面开发"工作负载) vcpkg 最新版 CMake >= 3.20 (VS2022 自带即可) 第一步:安装依赖 (vcpkg) 1 2 3 4 5 6 7 # 如果还没装 vcpkg git clone https://github.com/microsoft/vcpkg.git C:\vcpkg cd C:\vcpkg .\bootstrap-vcpkg.bat # 安装 Hical 所需依赖(x64-windows 默认 triplet) .\vcpkg install boost-asio boost-system boost-json boost-beast openssl zlib --triplet=x64-windows 第二步:获取 Hical 源码 1 2 git clone https://github.com/user/hical.git cd hical 第三步:CMake 构建 1 2 3 4 5 # 配置(替换为你的 vcpkg 实际路径,-DHICAL_BUILD_TESTS=OFF 跳过测试编译,加快构建速度) cmake -B build -A x64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DHICAL_BUILD_TESTS=OFF # 编译 cmake --build build --config Release 编译成功后,可执行文件位于: ...

May 12, 2026 · 4 min · 802 words

连接级 Atomic 时间戳超时的实现决策

起因 最初 Hical 的空闲超时实现就是传统做法:每个 HTTP 请求/每次 keep-alive 读等待都注册一个 steady_timer,读完成后取消,超时则关闭连接。实现上用的是 shared_ptr<function> 自引用环做回调链续期——每连接 2 次堆分配(shared_ptr 控制块 + function 对象),且每次续期都要重新构造回调。 v2.5.2 压测到 132K QPS 时,做热路径Review代码发现这个 timer 机制的问题: 每请求 2 次 epoll_ctl(注册 + 取消 timer) shared_ptr<function> 自引用环本身就有堆分配开销 140K QPS 下整体约产生 100 万次 epoll_ctl/sec,38% CPU 花在内核 _raw_spin_unlock_irqrestore(TCP spin_lock),而用户态框架代码只占不到 5% 瓶颈已经从用户态转移到内核态,减少进内核的次数成为核心策略。空闲超时的 timer 是明确可以砍掉的——30-60s 的超时精度要求本来就极低。 改良过程 分两步走: 第一步:先把 shared_ptr<function> 回调链改为独立协程 idleTimerLoop,消除自引用环和 2 次堆分配。这一步还是 per-connection 一个 timer 协程,只是实现更干净了。 第二步:发现即便使用协程,per-connection timer 仍然意味着每次 timer 到期时要走 scheduler 调度 + epoll_ctl。最终演化为"TcpServer 统一扫描"的设计——整个 server 只需要一个扫描协程,连接侧只写一个 atomic 值。 ...

May 12, 2026 · 2 min · 314 words

Hical v2.6.0:移除 Boost.Beast,自研 HTTP/WebSocket 栈全记录

Hical v2.6.0:移除 Boost.Beast,自研 HTTP/WebSocket 栈全记录 Hical v2.6.0 完成了一次关键架构升级——彻底移除 Boost.Beast 依赖,HTTP 解析/序列化和 WebSocket 全部替换为自研实现。本文从动机、架构设计、关键技术细节、性能数据四个维度,完整记录这次"去 Beast"的工程实践。 目录 Hical v2.6.0:移除 Boost.Beast,自研 HTTP/WebSocket 栈全记录 目录 1. 为什么移除 Boost.Beast 2. 新架构总览 3. 自研 HTTP 解析栈 3.1 picohttpparser:极致轻量的 C 解析器 3.2 零拷贝 NativeRequest 3.3 HeaderMap:L1 友好的头部容器 4. 自研 HTTP 响应序列化 4.1 栈缓冲 + Scatter-Gather I/O 4.2 状态行预计算 5. 自研 WebSocket 栈(RFC 6455) 5.1 帧解析器:WsFrame 5.2 握手协议:WsHandshake 5.3 Permessage-Deflate 压缩 5.4 消息分片重组 5.5 协议安全校验 6. 编译防火墙与工程化 7. 性能对比数据 火焰图逐项对比 QPS 端到端对比 与同类框架对比 8. 迁移影响与 Breaking Changes 对框架使用者 对框架使用者透明的改动 9. 新增依赖 picohttpparser zlib 10. 总结 1. 为什么移除 Boost.Beast Beast 是一个优秀的 HTTP/WebSocket 库,但在 Hical 的高性能场景中,它成了主要瓶颈: ...

May 11, 2026 · 9 min · 1738 words

C++ Web 服务日志最佳实践:Hical 日志系统完全指南

C++ Web 服务日志最佳实践:Hical 日志系统完全指南 引子:生产环境 printf 调试?该升级了 不少 C++ 服务器项目在早期会这样写日志: 1 2 printf("[INFO] user login: uid=%d\n", uid); fprintf(stderr, "[ERROR] db connect failed\n"); 这没什么问题——直到你的服务跑到生产环境,遇到以下场景: 日志文件膨胀:跑了三天,单个 app.log 已经 8GB,grep 一下需要等几分钟 性能抖动:每次写日志都 fwrite + fflush,高并发时 I/O 成为瓶颈 信息不够:出了问题只知道"某某接口报错",不知道是哪个请求、哪个用户 无法动态调级:想临时开 DEBUG 排查问题,必须重启服务 日志散落各处:访问日志、审计日志、业务日志混在同一个文件里,难以分析 Hical 的日志系统正是为了解决这五个问题而设计的。本文从最简用法出发,逐步覆盖文件轮转、异步写盘、结构化日志、通道分流、HTTP 集成到运行时调级,每个场景都给出可直接复制的代码。 1. 快速上手:三种 API 对比 Hical 日志提供三种书写风格,适用不同场景: std::format 风格(首选) 1 2 3 4 5 #include <hical/Log.h> HICAL_LOG_INFO("server started on port={}", 8080); HICAL_LOG_WARN("connection pool low, available={}", pool.available()); HICAL_LOG_ERROR("db query failed: sql={} err={}", sql, ec.message()); 格式字符串在编译期校验(借助 std::format_string<Args...>),参数类型不匹配直接报错,不会等到运行时才崩溃。这是最常见的用法。 ...

May 6, 2026 · 6 min · 1208 words

Hical 协程入门:告别回调地狱,用 co_await 写异步 C++

Hical 协程入门:告别回调地狱,用 co_await 写异步 C++ 传统 C++ 异步编程离不开回调嵌套、状态机、手动生命周期管理——代码写得像意大利面。C++20 协程从根本上改变了这一切:异步代码写起来和同步一样直观,编译器帮你管理暂停与恢复。本文从零讲解如何在 Hical 框架中使用协程,不需要你懂 Boost.Asio 底层。 什么是协程?30 秒版本 传统回调式: 1 2 3 4 5 6 7 8 9 10 11 12 // 回调嵌套——"回调地狱" socket.async_read(buffer, [&](error_code ec, size_t n) { if (!ec) { socket.async_write(buffer, [&](error_code ec2, size_t) { if (!ec2) { socket.async_read(buffer, [&](error_code ec3, size_t) { // 继续嵌套... }); } }); } }); 协程式: 1 2 3 4 // 同样的逻辑,协程版——像写同步代码一样 auto n = co_await socket.async_read(buffer, use_awaitable); co_await socket.async_write(buffer, use_awaitable); auto n2 = co_await socket.async_read(buffer, use_awaitable); co_await 会暂停当前函数,等 I/O 完成后自动恢复执行。没有回调,没有嵌套,错误用 try/catch 处理。 Hical 对协程做了什么封装? Hical 在 Coroutine.h 中提供了三个核心工具: ...

May 5, 2026 · 4 min · 762 words

C++ 也能优雅写 Web?5 分钟用 Hical 搭建 REST API

C++ 也能优雅写 Web?5 分钟用 Hical 搭建 REST API 提到 C++ 写 Web 服务,你脑海中浮现的可能是满屏的模板报错、手动解析 HTTP 报文、以及回调嵌套到看不清缩进的代码。但在 2026 年,C++20 协程 + PMR 内存池 + C++26 反射的组合,已经让 C++ Web 开发体验发生了质变。本文用 Hical 框架带你体验:10 行代码启动 HTTP 服务器,40 行代码搞定完整 REST API。 10 行代码,启动 HTTP 服务器 1 2 3 4 5 6 7 8 9 10 11 12 13 #include "core/HttpServer.h" using namespace hical; int main() { HttpServer server(8080); server.router().get("/", [](const HttpRequest&) -> HttpResponse { return HttpResponse::ok("Hello, hical!"); }); server.start(); } 1 2 curl http://localhost:8080/ # Hello, hical! 没有工厂类,没有 Builder 链,没有 XML 配置。创建服务器、注册路由、启动 —— 三步完事。 ...

May 4, 2026 · 4 min · 666 words

告别手写 API 文档:Hical OpenAPI 自动生成 + Swagger UI 一键集成

告别手写 API 文档:Hical OpenAPI 自动生成 + Swagger UI 一键集成 你是否经历过这种情况:花了半天写好 Swagger 注解,一个需求变更,参数名改了,文档却忘了同步——测试组拿着旧文档联调,来回扯皮两小时? API 文档和代码永远对不上,是后端开发者的经典痛点。本文介绍如何用 Hical 框架的 OpenAPI 模块,让文档从代码中自动生成,彻底消灭这个问题。 一、背景:OpenAPI 3.0 是什么 OpenAPI 3.0(即过去的 Swagger 规范)是描述 HTTP API 的行业标准 JSON/YAML 格式。有了它: Swagger UI / Redoc 可以直接渲染成可交互的文档页面 前端可以一键生成 TypeScript 类型定义 QA 可以直接在浏览器里填参数发请求 手写 OpenAPI YAML 很繁琐,维护成本高。Hical 的方案是:从 C++ 类型系统直接推导出 schema,标注一次,文档自动生成。 二、三步集成概览 1 2 3 步骤 1 定义 DTO,加 HICAL_JSON + HICAL_SCHEMA_NAME 步骤 2 标注路由,加 HICAL_API + builder::* 步骤 3 main() 中 registerRoutesWithOpenApi + serveOpenApi 不需要任何新依赖,OpenAPI 模块默认随 Hical 一起编译(HICAL_WITH_OPENAPI=ON 是默认值),底层复用已有的 Boost.JSON。 ...

May 1, 2026 · 5 min · 1023 words