Paozhu vs Drogon —— 系统设计对比

基于源码实测(paozhu(https://github.com/hggq/paozhu),drogon(https://github.com/drogonframework/drogon),均取 2026-10 最新源码目录)。

源码目录分析:drogon-master 经核查为 drogon 1.9.14(2026-10-11),其 lib/ 与 trantor/ 中不含 HTTP/2 服务器实现(无 Http2Server/Http2Connection;lib/src/HttpServer.cc 仅输出 HTTP/1.1,无 h2/ALPN 注册;trantor 只有 TLS 层的 ALPN 能力桩)。paozhu 则内置 HTTP/2(http2_parse.cpp 等)。故下文对 drogon 的 HTTP/2 描述以「drogon-master 实际代码」为准——drogon 主线截至 1.9.14 尚未内置 HTTP/2 服务器实现,paozhu 的 HTTP/2 是两者间的实际差异化能力。


0. 一句话定位

  • Drogon:典型「muduo 风格」+ 反射宏注册 + 编译期代码生成,偏工业级、生态完整、上层 API 友好。
  • Paozhu(炮竹):单 io_context + Asio C++20 协程贯穿全栈(连 ORM 查询都是协程),HTTP/1.1 与 HTTP/2 均自研,偏「全异步协程 + 多租户」。

1. 并发 / IO 模型(核心差异)

维度 Paozhu Drogon(本仓)
底层库 standalone Asio(#include ) trantor 自研网络库(基于系统 epoll/kqueue 封装)
事件循环拓扑 单一共享 io_context,N 个 worker 线程各跑 io_context.run() 每线程一个 EventLoop(one loop per thread,muduo 风格),多 ioLoop 并行
连接分发 连接挂在 asio::strand(client_session.h:194)保证协议次序 TcpServer 用 nextLoopIdx_ 轮询把新连接分给某个 ioLoop(TcpServer.cc:65)
跨线程通信 协程 co_await + strand 串行化 无锁 MPSC 队列 funcs_ + wakeupFd_(EventLoop.h:311-321)
业务线程 独立业务线程池 clientrunpool(server.h:487,hardware*2+2,可动态扩缩),同步 handler 进池跑 handler 默认跑在所属 ioLoop 线程,可手动 offload / 协程切线程
线程配置 cothreadnum、threadmax(conf/server.conf) numThreads / EventLoopThreadPool 大小

设计哲学差异:

  • Drogon 的「多独立循环」更贴合 Reactor 经典模型,单连接全程绑定一个线程,无锁、缓存亲和好。
  • Paozhu 的「单 io_context 多 worker」是 Asio 经典模型,协程帧在堆上、不占 io 线程;业务与 IO 显式分层(io 协程 vs 业务 clientrunpool),router::co_resolve 会逐步派单:协程步就地 co_await,同步步进业务池(server.cpp:2351 注释)。

2. HTTP 协议层

维度 Paozhu Drogon(本仓)
HTTP/1.1 自研解析 http_parse.cpp(122KB) + request.cpp/httppeer.cpp 自研状态机 HttpRequestParser.cc(kExpectMethod→…),支持 pipeline/stream
HTTP/2 内置:http2_parse.cpp(192KB) + http2_frame/huffman/ring_queue/send_queue,含 HPACK、ALPN、h2c,http2_enable=1 drogon-master 的 lib/ 无 HTTP/2 服务器实现(无 Http2Server/Http2Connection;HttpServer.cc 仅输出 HTTP/1.1,无 h2/ALPN 注册);trantor 仅含 TLS 层 ALPN 能力桩。
请求/响应模型 httppeer(httppeer.h:41,enable_shared_from_this)持有 urlpath/get/post/output/session 抽象类 HttpRequest/HttpResponse + Impl(HttpRequestImpl/HttpResponseImpl,renderToBuffer 序列化)
注意点 request.h 实为内置 JSON 微对象 obj_val,不是 HTTP 请求体 通过 HttpRequestPtr 智能指针传递;fromRequest/toRequest 隐式转换

说明:request.h 命名易误导——paozhu 真正的 HTTP 请求模型是 httppeer。


3. 路由系统(深度)

两者都是「声明式路由注册」,但机制分叉:Drogon 走编译期反射 + 宏 + 静态自注册;Paozhu 走源码注解 + 构建期 codegen 生成 reg_raw 表。此外 Paozhu 额外内置了基于 httppeer::flow_method 的简单任务编排(见 §3.4),Drogon 无对等机制。

3.1 注册方式与代码生成

Paozhu(controller/src/*.cpp 写 //@urlpath(pre,urlpath):mask,构建期 paozhu_pre 扫描):

  • 生成产物 common/autocontrolmethod.hpp 中是一张 reg_raw 调用表(autocontrolmethod.hpp:127-141):reg_raw(site, path, pre_sync, co_pre, reg_sync, co_reg, mask_code)。每个路由登记 5 个槽位:同步 pre、co_await 版 pre、同步 handler、协程 handler、以及 //@urlpath 注解里的数字 mask_code(区分 HTTP method)。
  • 带 :param 段的路径紧随其后用 reg_urlpath(site, path, {param_names...}) 单独登记参数名表(autocontrolmethod.hpp:139-140)。
  • 注册期还生成 common/autorestfulpaths.hpp、common/reghttpmethod.hpp 等辅助表。

Drogon(HttpController.h 宏,编译期):

  • METHOD_ADD(method, pattern, ...) / ADD_METHOD_TO(method, path, ...)(HttpController.h:31-34)→ registerMethod(&method, pattern, {...}, ...);还支持 ADD_METHOD_VIA_REGEX(:35)写正则。
  • 静态自注册:每个 controller 含 class methodRegistrator + static methodRegistrator registrator_(HttpController.h:127-139),构造函数里把方法名 + 参数包登记进框架——在 main 之前由 CRT 静态初始化完成,零运行时扫描。类名到路径的映射由 DrClassMap::demangle(typeid(T).name()) 自动推导(HttpBinder.h:235),:: 转 /。

3.2 匹配结构

Paozhu:核心 router 类(router.h:32),运行时把 reg_raw 填进 _handlers / _urlpath_map / _site_to_slot(全局 slot 0,多租户见 §3.5);co_resolve(peer, known_idx)(router.h:79)协程驱动整条匹配+pre+handler 链,并带 route_exact_checked 优化(httppeer.h:298-299,rpc/同步链跳过重复精确查找)。

  • 找不到精确匹配时前缀回退最多 6 段(router.cpp 两级查表:lookup_exact → lookup_registered → lookup_key_in_slots,router.cpp:180-185),未注册的跳转名在编排排空时被跳过(router.cpp:350-363)。

Drogon:HttpControllersRouter + StaticFileRouter。三套结构(HttpControllersRouter.cc):

  • simpleCtrlMap_:无参数精确匹配(小写 key 的 std::unordered_map)。
  • ctrlMap_:带参数但结构固定的精确路由,ctrlMap_.find(loweredPath) 直接哈希命中(HttpControllersRouter.cc:626)。
  • ctrlVector_:含 :param / 正则的路由,编译期把 {param} 占位符换成 ([^/]*)(regex_replace,:564),运行时 std::regex_match 线性匹配(:635-640)。
  • 每路由 binders_[req->method()] 按 HttpMethod 分桶;initMiddlewaresAndCorsMethods 自动注入中间件并开 OPTIONS/CORS(:58/63/75)。

3.3 预处理链(pre handler)与同步/协程混编

Paozhu —— 每条路由拆成 4 个独立槽位,同步与协程是「路由层一等公民」:reg_raw(site, path, pre_sync, co_pre, reg_sync, co_reg, mask_code)(common/autocontrolmethod.hpp)把一条路由注册为 同步 pre / 协程 pre / 同步 handler / 协程 handler 四个独立入口。主驱动 co_resolve(router.h:79,router.cpp:694)透明交织二者:协程步就地 co_await(pre co_await h.co_pre(peer) router.cpp:750-755;handler co_await h.co_regfun(peer) :806-811),同步步交给业务线程池执行;老版纯同步链 resolve() 已 #if 0 停用(:537 与其调用点 ThreadPool::http_clientrun 一并停用)。

  • 同步/协程混编是内置能力:同一请求里「同步 pre 鉴权 → 协程 handler 查库」「协程 pre → 同步 handler」都可直接声明,框架按各入口的 pre_kind/reg_kind 自动分流,开发者无需自己切线程或包 std::async。这与 Drogon「handler 选一种形态、CoroMapper 是另一个类型」的设计思想不同——Paozhu 把「是否协程」下沉为每个注册入口的属性,而非整条 handler 的类型。
  • 自带的 urlpath pre 拦截:每个路由的 pre_sync/co_pre 按声明顺序串成串行 pre 链;classify_step() 据返回值判定走向——@ok(放行)、同名(继续)、@exit(显式终止)、跳转名(router.cpp:76-87),首个非空即短路,空串是唯一放行信号(router.cpp:458-461)。此外还有站点级 pre/after hooks:run_site_hooks(同步 :462)与 co_run_site_hooks(协程 :478)批量跑 action_pre_lists/action_after_lists,超限时截断到前 site_hook_limit 条而非整条丢弃(避免鉴权静默失效)。
  • flow 栈排空:编排栈排空时(pop_flow_index),若驱动是同步链却弹到协程条目则停止排空(:364-368),协程驱动则就地 co_await 继续(:865-873)。

Drogon:pre 逻辑由 Filters(HttpFilter + ADD_FILTER_TO / GlobalFilters.cc)与 Middlewares(HttpMiddleware,MiddlewaresFunction.cc + HttpAppFramework.h:718)承载,框架在 handler 前按 path/全局顺序执行,与路由表解耦、可复用、可排序。同步/异步的选择落在单个方法形态上(binder 的 Blocking/Callback/future 三态,或协程用独立的 CoroMapper),没有「一条路由同时声明同步 pre + 协程 handler」这种按入口拆分的混编槽位。

3.4 简单任务编排(Paozhu flow_method)—— 是,Paozhu 支持

结论:是的,Paozhu 在 httppeer 上内置了简单任务编排,通过 httppeer::flow_method 实现「单请求内多 handler 顺序编排」。

  • 结构:httppeer 持有一个懒分配的栈 std::unique_ptr> flow_method(httppeer.h:300-301)——存的是后续要执行的 handler 名。
  • 入栈 API(httppeer.cpp:2135-2155):
    • push_next(name):push_back(栈顶,LIFO,后压先出)。
    • push_last(name):push_front(栈底,FIFO,先压先出——顺序拆解的默认推荐)。
    • push_steps({...}):批量压入。
  • 出栈/消费(httppeer.cpp:2156-2169 pop_flow() 取 back 弹出;router.cpp:340-368 pop_flow_index):一个 handler 正常完成(返回空串)后,路由器从栈弹下一个已注册 handler 名并接着执行;一个 handler 只弹一个,剩余等它自己「返回空串」再弹,直到栈空或整链触顶(router.cpp:651-657 同步链、834-839 协程链)。
  • 流程控制指令:kFlowOk / kFlowExit(显式终止、不弹栈)/ kFlowTimeloop(转事件循环)由 classify_step 解释(router.cpp:76-87)。
  • 健壮性:弹出未注册名时跳过继续弹下一个(不吞掉整条编排,:350-363);协程条目压到同步链上按设计停下(:364-368)。

这意味着一个请求可以「handler A → 压 B、C、D → A 返回空串后依次跑 B→C→D」,构成轻量顺序工作流 / pipeline。Drogon 没有对等的内建机制:它的组合原语是 Filters/Middlewares(横切关注,非顺序步骤编排)与 handler 自身逻辑;若要在 Drogon 里串多个 handler,需 handler 内手动调用或重定向,框架层不提供编排栈。

3.5 多租户 / 站点 slot

Paozhu:路由天然挂在站点 slot 上——host_index → slot,_site_to_slot 每站点一条 slot,路由表按 slot 隔离(router.cpp:337 find_name_in_slots(peer_slot(peer), ...));reg_raw 第一个参数就是 site(域名,空串=全局 slot 0)。配合 §9 的主题视图,是框架级多租户。 Drogon:路由本身不带租户概念;多租户靠「多 app / 多 controller 分组 + DrObject 命名」在业务层实现,无一站式 slot 隔离。

3.6 小结对照表

维度 Paozhu Drogon(drogon-master 1.9.14)
注册机制 源码 //@urlpath 注解 + 构建期 codegen 生成 reg_raw 表 反射宏 METHOD_ADD/ADD_METHOD_TO + 静态自注册(methodRegistrator,main 前)
路径参数 :param 占位 + reg_urlpath 参数名表 {param} 占位,编译期转 ([^/]*) 正则
精确匹配 _urlpath_map + 前缀回退(最多 6 段) simpleCtrlMap_/ctrlMap_ 哈希;ctrlVector_ 正则
Pre/横切 每路由 pre_sync/co_pre 串行链(@ok/@exit/跳转)+ 站点级 pre/after hooks Filters + Middlewares(可排序、可复用、与路由解耦)
同步/协程混编 路由层一等公民:reg_raw 拆同步 pre/协程 pre/同步 handler/协程 handler 四槽位,co_resolve 透明交织 同步/异步落在单方法形态(Callback/future/CoroMapper),无按入口拆分的混编槽位
简单任务编排 有:httppeer::flow_method 栈(push_next/push_last/pop_flow),单请求多 handler 顺序执行 无内建;靠手动调用/重定向
多租户路由 站点 slot 隔离(_site_to_slot) 无;业务层分组

核心差异:Drogon 的路由是「宏反射 + 静态注册 + Filters/Middlewares」的工业级经典范式,关注点分离干净;Paozhu 是「注解 + codegen + 站点 slot + flow_method 编排」的一体化范式,额外给了请求内顺序任务编排这一轻量工作流能力,且多租户在路由层即内建。


4. ORM 深度对比

二者均支持 MySQL / PostgreSQL / SQLite 三方言,且 ORM 查询都支持 C++20 协程异步。但在生成时机、代码组织、查询风格、缓存、事务、关系表达上差异显著。

4.1 生成代码结构

Paozhu(paozhu_cli orm ,生成器 vendor/httpcli/modelfun.hpp)每张表产出三层:

  • _base.h:纯数据/反射层——namespace _info 含 col_count、enum cols、struct meta(字段定义)、type:: 类型别名、col_names、null_col 位图、assign_field_value(world_base.h:41-45、L76-80),以及脏字段位图 __dirty_bits + get_dirty_indices()/clear_dirty()/make_update_dirty_sql()。
  • _opsql.h:template class _opsql : public B_BASE(含全部查询/缓存/脏更新方法,构造时按 dbtag 从 get_orm_conn_pool_obj() 取 orm_conn_pool)。
  • 脏更新机制:setXxx() 写字段时标记对应脏位;update_dirty()(文本协议)/ exec_update_dirty()(预编译)/ async_update_dirty() / async_exec_update_dirty() / commit_update_dirty() 仅对脏位字段拼 UPDATE,执行成功后 clear_dirty();脏位为空时短路、不发出全字段误更新,且缺 WHERE 时拒绝执行(article_opsql.h:9866/15248/10319/15323/13120)。这与 Drogon 的 dirtyFlag_[] 机制等价,都是「只写变化列」的增量更新。
models/include/.h:薄壳 class : public _opsql<,_base>(models/include/World.h:11)。 FK 关系在外联 .cpp:orm/fk_child_opsql.cpp、orm/fk_parent_opsql.cpp(见 §4.6)。

Drogon(drogon_ctl create_model,复用 CSP 模板 model_h.csp/model_cc.csp)每张表生成一个模型类(示例 orm_lib/tests/mysql/Users.h):

struct Cols(列名常量)、表元数据(tableName/primaryKeyName/PrimaryKeyType:单主键为具体类型,复合为 std::tuple,无主键为 void)。 每字段三件套:getValueOfXxx() / getXxx() / setXxx()+setXxxToNull()(nullable)。 JSON 互转与校验:toJson()/updateByJson()/validateJsonForCreation/Update。 私有持久化友元方法:outputArgs/updateColumns/updateArgs/insertColumns,由 Mapper 内部调用。 脏标记 dirtyFlag_[]:决定 insert/update 只写变化列(Users.h:252-274)。 静态 SQL 字符串:sqlForFindingByPrimaryKey()、sqlForDeletingByPrimaryKey()、sqlForInserting(bool& needSelection)(按 dirtyFlag_[] 动态拼装列;PG 追加 returning *,MySQL/SQLite 依赖 LAST_INSERT_ID/insertId())。

关键差异:Paozhu 把「数据定义(base)」与「SQL 操作(opsql)」分离成两个文件并支持外联关系 .cpp;Drogon 把所有东西塞进一个模型类,持久化细节用 private + friend 封装,更面向对象。

4.2 查询 API 风格

Paozhu —— 链式 builder(返回 M_MODEL&),方法名偏「直接 SQL 动词」:

读:count()(L175) / exec_count()(L284) / page()(L349) / fetch()(L5631) / fetch_one()(L6702) / fetch_to()(L5149) / fetch_one_to()(L6225)。 写:update_col(L697) / exec_update_col(L821) / replace_col(L1073)。 条件:where()(L2017) / whereIn(L2155) / whereNull(L2240) / whereEQ(L2368) / 每列快捷式 eqId()(L1538 起);条件运算符 orm::wq::eq/nq/bt/be/lt/le。 JOIN:leftJoin()(L10367)。 异步:async_fetch(L5781) / async_count(L434) / async_page(L612),及带 _prepared 的 exec_*。 分页自动复用已有条件:page()(L349)/async_page()(L612) 内部先调 count()(L175),而 count() 经 build_text_where(where_clause)(L178) 读取模型上已用 .where() 设好的筛选;随后 fetch() 也走同一 build_text_where——COUNT 与数据查询共用同一 where,条件只设一次。page() 还直接返回 (minpage,maxpage,page,total_page) 导航四元组(:396),无需手写分页数学。

Drogon —— 流式 Mapper + Criteria(模板 Mapper 在 orm/Mapper.h:115,构造收 DbClientPtr):

读:findByPrimaryKey(L269) / findAll(L434) / count(L459) / findOne(L492) / findBy(L523),各方法三种形态(同步/回调/future)。 写:insert(L552) / update / updateBy / increment(L662) / deleteOne/deleteBy/deleteByPrimaryKey(L707-789)。 条件 Criteria(Criteria.h:73):CompareOperator 含 EQ/NE/GT/GE/LT/LE/Like/In/IsNull/IsNotNull;支持 operator&&/|| 组合、CustomSql+_sql 字面量、Criteria(Json::Value)。 流式 builder:limit/offset/orderBy/paginate/forUpdate/innerJoin/leftJoin/rightJoin(L134-252),占位符 $? 按 DB 替换为 $1,..(PG)或 ?(MySQL/SQLite)。

风格差异:Paozhu 偏向「熟悉的链式动词 + 每列快捷方法」,上手成本低;Drogon 用 Criteria 表达式对象 + Mapper 流式设置,类型安全更强、但需理解 Criteria/SqlBinder 体系。

分页对比:Paozhu 的 page() 自动复用已设 where,一次调用完成「计数 + 取数 + 返回导航四元组」;Drogon 的 paginate(page,perPage)(Mapper.h:173/2013)仅设 offset/limit,count() 默认 Criteria() 空条件(Mapper.h:459),需把同一 Criteria 分别传给 count(criteria) 与查询,分页导航(总页数/窗口)要自己算。

4.3 缓存机制(最大功能差异)

Paozhu Drogon(本仓) 查询结果缓存 有,内建 无(ORM 层零缓存) 实现 vendor/httpserver/include/orm_cache.hpp:model_meta_cache(std::map,L27/L139),方法 try_get/save/remove/check/update(L274/137/194/233/250) orm_lib 中 `cache 接入 use_cache()(L7034) / save_cache(exptime)(L7116) / get_record_cache()(L7215) / try_get_cache()(L7201) / remove_cache()(L7066) — 键 std::hash{}(sqlstring)(L5674)或 cache_key_name;命中时 fetch() L5672-5680 返回并置 is_hit_cache — 后台回收 detach 线程每 30s remove_exptime()(L74/L208) —

Paozhu 缓存已知局限(实测):

save_cache 仅由 iscache 触发、不检查 effect_num,故 effect_num==0 的查询仍会被缓存(与「effect_num==0 不缓存」的防护期望相反)。 get_record_cache() 仍走「抛异常判定 miss」的旧路径(try_get_cache 才是新路径)。 cover_data 滑动过期(save() L137/L154);exptime==0 为永久键。

结论:Paozhu 自带查询级结果缓存(可显著降 DB 压力,但需注意上述局限);Drogon 把缓存交给上层(如 Redis / 业务层),ORM 本身每次查询都打连接池。

4.4 异步与协程模型

Paozhu —— 全协程(Asio co_await):

_opsql.h 的 async_fetch 内部 co_await select_conn->async_fetch_directly(...)(world_opsql.h:488/5781);DML co_await edit_conn->async_exec_dml(L970)。 连接池 orm_conn_pool.cpp:async_get_mysql_select_conn(L654) / async_get_mysql_edit_conn(L608) 用 co_await async_add_*_connect 取/建连接;同步 get_mysql_*_conn(L631/676)。 后端:pg_conn.cpp async_read_loop(L1802) co_await;sqlite_conn.cpp 经 run_on_worker 队列(L1675+)。

Drogon —— 三形态(同步 / 回调 / future),协程为叠加层:

每个 Mapper 方法都提供三套:binder << Mode::Blocking(同步抛异常)/ binder >> rcb >> ecb(回调)/ std::promise+get_future(future)。 底层 DbClient::operator<<(sql) 返回 SqlBinder(DbClient.h:256),<< 绑参、>> 注册回调、exec() 执行。 协程:CoroMapper(CoroMapper.h:67,继承 Mapper)返回 MapperAwaiter,await_suspend 把 callback 接入 CallbackAwaiter 并 resume();另有 execSqlCoro、newTransactionCoro。 结果抽象:Result(Result.h:57,引用计数,含 size()/affectedRows()/insertId() 等多态实现)/ Row(Row.h:46,operator[] 返回 Field)。

4.5 事务

Paozhu Drogon(本仓) 机制 模型侧 lock_conn(shared_ptr txn_conn)(如 github_repo_opsql.h:11244)挂接外部事务连接;unlock_conn()(L11259) 仅解绑、不归还池(防未提交会话泄漏) Transaction 对象(DbClient.h:419,继承 DbClient);Mapper(transactionPtr) 即让后续读写在同事务连接上 边界驱动 orm::db_conn 的 begin_commit()/commit()(mysql_conn.cpp:4307);in_transaction_ 状态(L337/4304);sqlite_conn.cpp async_begin/commit/rollback_transaction(L1693-1707) newTransaction/newTransactionAsync/newTransactionCoro;TransactionImpl 从 readyConnections_ 借出单连接移入 busyConnections_,BEGIN/BEGIN IMMEDIATE/BEGIN EXCLUSIVE;提交/回滚后经 usedUpCallback 归还 隔离级别 依赖后端 TransactionType:Deferred/Immediate/Exclusive

差异:Drogon 用一等 Transaction 对象塞进 Mapper,API 干净、可组合;Paozhu 是把事务连接 lock 到模型上,更手动、需显式 unlock 防泄漏。

4.6 关系 / 外键

Paozhu:oneXXX/manyXXX 在外联 .cpp(orm/fk_child_opsql.cpp:oneFkParent(L9)/manyFkParent(L38);orm/fk_parent_opsql.cpp:oneFkChild(L9)/manyFkChild(L38)),实现即用本表外键值对关联模型 where()/whereIn();另提供通用 leftJoin() 做显式 JOIN。

Drogon:关系是「生成期配置 + 运行时每模型方法」,无 hasMany/hasOne 风格的通用 Mapper 关系 API。

配置结构 Relationship(create_model.h:211,Type{HasOne,HasMany,ManyToMany} + PivotTable 多对多中间表)。 FK 自动探测:tryAddAutoRelationship(create_model.cc:181);PG 查 information_schema 外键视图、MySQL 查 KEY_COLUMN_USAGE、SQLite 查 PRAGMA foreign_key_list。 生成方法体(model_cc.csp:1580-1789):按类型生成 getXxx(clientPtr) 同步/异步两版——HasOne 拼 select * from where = ?,HasMany 返回 vector,ManyToMany 联表 + pivot 返回 vector>。关系查询即 user.getWallet(client),内部仍走 DbClient/SqlBinder,可传事务 client。

4.7 类型映射与参数绑定

Paozhu:生成期 modelfun.hpp 用 pg_type_to_mysql_type()(L337) 与 SQLite 亲和性映射(L886+) 把 SQL 类型转 MySQL 二进制类型码,再 get_field_type()(L1232) 按码映射 C++(0x03→int、0x08→long long、0x0A-0x0D 日期→string、0xFC-0xFE varchar/text→string),最终由 _base.h struct meta + type:: 落地。

Drogon:生成期 create_model.cc 直接按库写死映射(PG L309-360 / MySQL L621-679 / SQLite L870-902)。参数绑定 SqlBinder::operator<<(SqlBinder.h:382-444)按 ClientType 分支:

PG:2/4/8 字节整数 htons/htonl/htonll 转网络序、format=1(二进制); MySQL:getMysqlType() 映射(含 static_assert 拒绝不支持类型); SQLite:按 sizeof(T) 选类型;另支持 trantor::Date/vector/optional/Json::Value。 DB 行→C++ 字段:Field::as()(Field.h:70-107,带大量特化)+ 模型构造 T(const Row&, indexOffset),对 date/bytea 做特殊解析,并据 dirtyFlag_[] 控制增量写列。

4.8 连接池

Paozhu:orm_conn_pool.cpp 按 dbtag 维护读写分离连接池(async_get_mysql_select_conn 读 / async_get_mysql_edit_conn 写),协程取/建连接(co_await async_add_*_connect);后端 pg_conn/mysql_conn/sqlite_conn 各自实现 async_* 非阻塞 I/O,由 init_orm_conn_pool(io_context,...)(server.cpp:9269)启动。

Drogon:DbClientImpl(orm_lib/src/DbClientImpl.cc)三态集合 connections_/readyConnections_/busyConnections_(互斥保护);execSql 有空闲则取用,否则压入 sqlCmdBuffer_(上限 200000,超限报 “Too many queries in buffer”);按 connNum 决定事件循环数(SQLite 固定 1 个,PG/MySQL 取 hardware_concurrency() 与 connNum 较小值)。各库连接类 PgConnection/MysqlConnection/Sqlite3Connection 仅负责单连接非阻塞 I/O;支持 execSqlWithTimeout 超时抛 TimeoutError。

底层协议实现差异(架构级):

Paozhu 自研 MySQL / PostgreSQL / SQLite 线层协议:mysql_conn.cpp 手写 read_server_hello(解析 capability_flags、auth_plugin_salt_data)、caching_sha2_password 认证握手、mysqlnd_xor_string 加密、seq_next_id 包序号;pg_conn.cpp 手写 build_startup_message、SCRAM-SHA-256 握手(sasl_scram_sha256、int32_to_buf 消息帧);sqlite_conn.cpp 同理。全仓 CMake 不链接 libmysqlclient/libpq/sqlite3——协议细节完全自研。 Drogon 封装官方客户端库:MysqlConnection 直接 #include 调 mysql_real_connect/MYSQL*(CMakeLists.txt 链 MySQL_lib/unofficial::libmariadb);PgConnection 用 libpq(find_package(pg));SQLite 用 SQLite3_lib。线层协议交给第三方库,Drogon 只做异步封装(基于 mysql_real_connect_start/cont 等非阻塞 API)。

4.9 业务代码风格(实际写法)

§4.2 讲的是 API 形态,这里给出真实业务代码长什么样(均取自两侧源码示例)。

Paozhu —— 模型对象即查询构建器,链式动词 + 协程/同步双形态(示例 controller/src/testormcache.cpp / testcrud.cpp):

// 模型对象本身就是 builder;条件、缓存、查询一气呵成
Article art;
art.where("isopen", 1).whereEQ(art.Alias::aid, aid).use_cache();  // 链式条件 + 查询缓存
co_await art.fetch_one();                          // 协程查询,结果回填 art
client << "
title:" << art.getTitle(); if (art.is_hit_cache()) { /* 命中缓存分支 */ }// 分页:直接返回 (bar_min, bar_max, current_page, total_page) unsigned int page = client.get["page"].to_int(); auto [lo, hi, cur, total] = art.page(page, 10, 5); // page, per_page, 导航栏宽度

风格要点:拿到模型实例 → 在其上 .where()/.whereEQ()/.use_cache()/.fetch_one()/.page() 链式拼装 → 协程 co_await 或同步 fetch_one();没有独立的 Mapper 层,模型即 DAO。

Drogon —— 泛型 Mapper + Criteria 表达式,按模型类型 CRUD(示例 orm_lib/tests/db_test.cc):

using namespace drogon_model::postgres;
Mapper mapper(clientPtr);          // clientPtr = app().getDbClient()
Users user;
user.setUserId("pg"); user.setUserName("alice");
mapper.insert(user);                      // 同步写入
auto u = mapper.findByPrimaryKey(2);      // 按主键查// 协程形态
CoroMapper cmapper(clientPtr);
auto cu  = co_await cmapper.findByPrimaryKey(2);
auto us  = co_await cmapper.orderBy(Users::Cols::_user_id).limit(2).findAll();
auto one = co_await cmapper.findOne(Criteria(Users::Cols::_id, CompareOperator::EQ, 1));

风格要点:Mapper 是独立于模型对象的通用 CRUD 门面,条件用 Criteria(列, 比较符, 值) 表达式构造;模型对象只承载数据 + setter/getter,所有持久化操作经 Mapper 发起。

一句话:Paozhu 是「模型自己会查自己」(builder 内聚在模型上,动词式、上手快);Drogon 是「Mapper 代理模型 CRUD」(泛型门面 + Criteria 表达式,类型安全、关注点分离更清晰)。

4.10 小结对照表

维度 Paozhu Drogon(本仓) 支持的 DB MySQL / PostgreSQL / SQLite(dbtype 配置) PostgreSQL / MySQL(MariaDB) / SQLite3 生成来源 读 schema//tables/*.sql(离线/CI 友好) 连活库读 information_schema/PRAGMA 产物组织 base(opsql 分离) + 外联 FK .cpp 单模型类 + private/friend 封装 + dirtyFlag 查询风格 链式动词 builder(where/page/fetch) Mapper + Criteria 流式表达式 查询结果缓存 内建 orm_cache.hpp/model_meta_cache 无(缓存交给上层/Redis) 异步范式 全 co_await(Asio) 同步/回调/future 三套 + CoroMapper 叠加 事务 lock_conn 挂事务连接到模型,手动 unlock 一等 Transaction 对象塞进 Mapper 关系/FK oneXXX/manyXXX 外联 .cpp 生成期 Relationship + 模型 getXxx(client) 方法 连接池 dbtag 读写分离协程池 DbClientImpl 三态池 + 超时 + 缓冲上限 类型映射 SQL→MySQL 二进制码→C++ 按库写死映射 + SqlBinder 二进制绑定 底层协议/客户端依赖 自研 MySQL/PG/SQLite 线层协议(mysql_conn.cpp/pg_conn.cpp 手写握手与认证),不链接官方客户端库 封装官方客户端库(MysqlConnection→libmysqlclient/libmariadb、PgConnection→libpq、SQLite→sqlite3),协议交给第三方

核心差异要点:

生成时机:Drogon 连活库反向工程;Paozhu 从 SQL schema 文件生成(与库解耦,CI 友好、可离线)。 抽象风格:Drogon 是模板 Mapper + Criteria 统一 CRUD(面向对象、类型安全);Paozhu 每个表生成独立 _opsql.h 操作类(动词式 builder、上手快)。 缓存是分水岭:Paozhu 自带查询级结果缓存;Drogon ORM 无内建缓存。 事务 API:Drogon 更优雅(Transaction 对象),Paozhu 更手动(lock_conn)。 脏更新(增量写列)两者都支持:Paozhu 用 __dirty_bits 位图 + update_dirty()/exec_update_dirty()(脏位为空短路、缺 WHERE 拒绝执行);Drogon 用 dirtyFlag_[] 控制 insertColumns/updateColumns。机制等价。 一致点:两者 ORM 查询都支持 C++20 协程异步,三方言均覆盖。

5. 流量限制对比

两者限流定位完全不同:Drogon 是请求级(per-IP / per-user / per-route,令牌桶/滑窗/固定窗,插件开箱即用);Paozhu 是连接接纳级过载保护(60 秒连接数窗口 + 负载自适应放行率)+ 站点上传限额 + IP 封锁。

5.1 Drogon —— 请求级 RateLimiter + Hodor 插件

核心类(lib/inc/drogon/RateLimiter.h):

RateLimiterType 三算法:kFixedWindow / kSlidingWindow / kTokenBucket(RateLimiter.h:10-15)。 工厂 RateLimiter::newRateLimiter(type, capacity, timeUnit=60s)(RateLimiter.h:42-45),接口 isAllowed()→bool(:52);SafeRateLimiter 包 mutex 做线程安全(:56-74)。 三实现:TokenBucketRateLimiter / SlidingWindowRateLimiter / FixedWindowRateLimiter(lib/src/ 下 .cc/.h)。

Hodor 插件(lib/inc/drogon/plugins/Hodor.h,Drogon 官方限流插件):

JSON 配置即启用,拦截走 AOP onHttpRequest(Hodor.h:144)AdviceCallback 前置建议,超限回 rejection_message(默认 "Too many requests")。 维度齐全(Hodor.h:36-77):algorithm(默认 token_bucket)、urls 正则(空=全部)、time_unit、capacity(全局)、ip_capacity(单 IP)、user_capacity(单用户,配 setUserIdGetter)、sub_limits(按 URL 分组子策略)、trust_ips/use_real_ip_resolver(XFF/真实 IP)、limiter_expire_time(默认 600s,用 CacheMap 存各 IP/user 的 limiter)。

Drogon 的限流是生产级、细粒度、声明式:配一段 JSON 即可对 /api/.* 做「全局 1000/s + 单 IP 100/s + 单用户 50/s」的组合策略,算法可换。

5.2 Paozhu —— 连接接纳级过载保护 + 上传限额 + IP 封锁

连接接纳限速(内建在 httpserver 核心,server.h:247 ratelimiter() 线程驱动):

60 秒流量窗口:update_minute_count()(server.cpp:8583-8632)——每个 accept 线程各持一份 60 桶 thread-local 环形,快照全局 total_count(连接计数),算出「最近约 60 秒进来的连接数」。 负载自适应放行率:compute_rate_from_load(has_save_link_count)(server.cpp:8636-8647)——把活跃连接数映射到 [lo,hi] 区间成百分比,再线性折成放行率 [num1,num2];阈值由 rate_limit_new_wait_num/rate_limit_accept_wait_num/rate_limit_second_num1/rate_limit_second_num2 控制。 排队与慢放:超过阈值时(has_save_link_count > rate_limit_new_wait_num && live_link_count > rate_limit_new_wait_num,server.cpp:9014-9022 / 9197-9204)调 enqueue_rate_limited()(:8650)把连接压入 socket_session_wait_rate,限速线程被 rate_limit_condition 唤醒后按 rate_limit_second_time_num 个/秒逐拍慢放(:8749-8758 的 slot 循环)。 性质:按连接数(而非请求数)的总量闸门,是「过载时平滑接纳新连接」的保护,不是「单 IP/单用户每秒 N 请求」的配额。

上传限额:httppeer::check_upload_limit()(httppeer.cpp:789,站点级 host_index 守卫)——按站点限制上传体大小。

IP 封锁:check_blockip(peer_session)(server.cpp:5757,TLS 握手失败连接不计入流量),配合黑名单/白名单做接入层拦截。

Paozhu 没有「per-IP / per-user / per-route 请求配额」这一层;要做单 IP QPS 限制需自己写中间件(利用 router 的 site pre hooks 或 flow_method 链)。

5.3 小结对照表

维度 Paozhu Drogon(drogon-master 1.9.14) 限流层级 连接接纳级(过载保护)+ 上传限额 + IP 封锁 请求级(per-IP / per-user / per-route 配额) 算法 固定放行率 + 负载自适应(compute_rate_from_load) 令牌桶 / 滑窗 / 固定窗(三选一) 度量窗口 60 秒连接数窗口 + 活跃连接负载 可配 time_unit(默认 60s),按请求计数 单 IP/单用户 无内建 内建(ip_capacity / user_capacity) 按路由细分 无(全站总闸门) urls 正则 + sub_limits 分组 启用方式 编译进 httpserver,调阈值参数 装 Hodor 插件 + JSON 配置即生效 超限响应 连接排队慢放 / IP 直接 stop 返回 rejection_message(默认 429 文本)

核心差异:Drogon 把限流做成业务友好的请求级配额(单 IP/用户/路由、可换算法、插件化);Paozhu 把限流做成系统级的连接过载闸门(防雪崩、平滑接纳),并附上传限额与 IP 封锁。两者不重叠——若 paozhu 需要「单 IP 100 QPS」,得用 router 的 site pre hooks 自写计数器;若 drogon 需要「连接总量过载保护」,得靠 OS/反向代理或自写 filter。

6. 协议与中间件支持(WebSocket / MQTT / Redis / RPC / Socket)

这一维差异极大:Paozhu 把五类协议全部内置到 httpserver 核心(且用统一的 subpub 注册模型贯穿 ws/mqtt/socket);Drogon 仅内置 WebSocket 与 Redis,MQTT / RPC / 裸 Socket 服务器均无框架级支持。

6.1 WebSocket

Paozhu(内置 server + client):websockets.h + ws_conn.cpp(连接)/ws_handshake.cpp(握手)/ws_parser.cpp + ws_wire.h(帧编解码)/http_websocket_client.cpp(65KB 客户端);websockets_callback.cpp + websockets.conf 配置;控制器层还提供 websockets/loopwebsockets.hpp / loopwebsockets_co.hpp(协程循环)/ wssleepwebsockets.hpp 等循环抽象。 Drogon(内置 server + client):WebSocketController.h(服务端控制器)/ WebSocketConnection.h(连接)/ WebSocketClient.h(客户端),对应 *Impl.cc;tests/ 下有完整 coro/keepalive/upgrade 测试。两者能力对等。

6.2 Redis

Paozhu(内置,vendor/pzredis/):完整客户端 redis_client.h + redis_conn.h(连接)+ redis_pool.h(按 section 多实例连接池)+ redis_pubsub.h/redis_subpub.h(发布订阅)+ redis_reply.h(回复解析)+ redis_types.h;同步版裸名 / 协程版 async_ 前缀(redis_client.h:294-296 asio::awaitable),配置 redis.conf。不依赖 hiredis,自研 RESP 解析。 Drogon(内置,nosql_lib/redis/):RedisClient + RedisConnection + RedisSubscriber + RedisTransaction + RedisClientManager;底层用 hiredis(cmake_modules/FindHiredis.cmake)。能力与 paozhu 对等,但借力第三方库。

6.3 MQTT

Paozhu(内置 broker + client,完整实现):mqtt_broker.h 是单例 broker(mqtt_broker::instance()),含订阅表 subscribe()/unsubscribe()/unsubscribe_all、retained 消息、topic 过滤匹配、publish() 投递、prune_expired() 过期清理,并带全局配额 kMaxSubscriptionsTotal=100000(:84-198);mqtt_session.cpp + mqtt_frame.cpp(35KB,MQTT 帧编解码,含 MQTT5 的 No Local / Retain As Published / Retain Handling / 共享订阅 group+device);http_mqtt_client.cpp 客户端 + mqtt.conf,控制器 mqtt/echo_mqtt_client.hpp/my_test_mqtt.hpp。即开即用的 MQTT 服务。 Drogon(无):drogon-master 全仓 0 个 MQTT 文件,框架不提供 broker 也不提供 client;需引入第三方插件(如社区 drogon-mqtt)或自行基于 WebSocket/TCP 实现。

6.4 RPC

Paozhu(内置自定义 RPC 协议):rpc_parse.h/rpc_parse.cpp 解析专属 rpc 线格式(请求行 + KV 头 + 二进制 body,含大小限制 CONST_RPC_*),http_rpcclient.cpp(73KB)承载客户端与连接逻辑,并按自身路由分发;控制器 test_rpc_handle.cpp 演示 handler。即在同一个连接层上提供 RPC 语义。 Drogon(无内置):lib/ 中唯一 rpc 命中是 Windows 平台的 #include (Utilities.cc:24,与 RPC 框架无关);无 protobuf / thrift / gRPC 封装。要做 RPC 需自行叠加(如基于 JSON + 自定义 controller,或外接 gRPC 进程)。

6.5 裸 Socket / TCP 服务器

Paozhu(内置):http_socket.h + http_socket_client.cpp 提供裸 TCP 连接承载;sock_subpub_reg.h/.cpp + sock_subpub_client.h 把 socket 接入统一的 subpub 注册/订阅模型,sockets_config.cpp 配置。即框架可同时作为 HTTP/WS/MQTT/Socket 多协议服务器。 Drogon(无):HttpAppFramework 仅 addListener() 启 HTTP 监听器(:864),无 createTcpServer/UdpServer/裸 socket 服务器 API;要裸 TCP 需直接基于 trantor 的 TcpServer 或自管 asio,框架不替你做协议分派。

6.6 架构主题:统一的 subpub 注册模型

Paozhu 在 ws/mqtt/socket 三处都使用 *_subpub_reg(ws_subpub_reg / mqtt_subpub_reg / sock_subpub_reg)——同一套「订阅注册 + 发布分发」内核贯穿多协议;Redis 也走 redis_subpub。这是 paozhu「协议全家桶」背后的统一抽象。Drogon 各能力彼此独立(WebSocket 控制器、Redis nosql 库、无 MQTT/socket),没有跨协议的统一 pub/sub 内核。

6.7 小结对照表

协议 / 中间件 Paozhu Drogon(drogon-master 1.9.14) WebSocket 内置 server + client 内置 server + client Redis 内置(pzredis,自研 RESP,按 section 池,sync/async) 内置(nosql_lib/redis,基于 hiredis) MQTT 内置 broker + client(MQTT5,含 retained/共享订阅) 无(需第三方插件) RPC 内置 自定义 rpc 协议(rpc_parse/http_rpcclient) 无内置(无 protobuf/thrift/gRPC) 裸 Socket / TCP 内置(http_socket + sock_subpub_reg) 无(仅 HTTP addListener,裸 TCP 需自管 trantor/asio) 跨协议统一抽象 有:ws/mqtt/socket/redis 共用 subpub 注册模型 无(各能力独立)

核心差异:Paozhu 是「多协议全家桶」——一个框架同时是 HTTP/2、WebSocket、MQTT(broker)、Redis、RPC、裸 Socket 服务器,且由统一 subpub 内核串联;Drogon 是「专注 HTTP 的 Web 框架」——WebSocket 与 Redis 一等公民,其余协议(MQTT/RPC/TCP)交由外部库或自行扩展,框架本身不内建。

7. 控制器与代码生成

维度 Paozhu Drogon(本仓) 代码生成器 构建期 paozhu_pre(CMake target paozhu_codegen,CMakeLists.txt:466-492),扫描 controller/src drogon_ctl 命令行工具 输入 controller/src/*.cpp 里的 //@urlpath(pre,urlpath) 注解 命令行手工/半自动生成骨架 增量 cache/method_notes.data(md5+mtime) 跳过未变文件(autopickmethod.cpp:170-288) — 生成产物 controller/include/.h(前向声明)+ common/autocontrolmethod.hpp(reg_raw 路由表) create_controller 骨架、create_view 把 .csp→DrTemplate 子类 模板引擎 view/ HTML 编译为 viewsrc/ C++ 源码(viewsrc/view/) CSP(C++ Server Pages):.csp 编译成 DrTemplateBase 子类 genText() 视图机制 view() / theme_view()(多主题前缀) DrTemplate + newTemplate()

差异要点:

Paozhu 的 codegen 是构建流水线的一环(每次编译主程序前自动跑,强制刷新路由表);Drogon 的 drogon_ctl 是开发者手动执行的 CLI,产物常驻仓库。 视图:Paozhu 自研 HTML→C++ 编译;Drogon 用 CSP(C++ 内嵌模板),两者思路都是「模板编译进二进制」,但语法/机制不同。

7.1 最小可运行示例(注解函数 vs 注册宏)

Paozhu —— 注解函数(取自 controller/src/testrand.cpp,真实最简写法):

#include "httppeer.h"
namespace http
{
//@urlpath(null,testrand)        // 站点(null=全局 slot) + URL 路径;无前置过滤器
std::string testrand(std::shared_ptr peer)
{httppeer &client = peer->get_peer();client << "

随机数: " << rand_range(100, 999) << "

";return ""; // 空串 = 正常完成/放行;非空串为跳转或终止 } }//namespace http 函数签名 std::string handler(shared_ptr) 即注册为同步 handler(reg_raw 的 reg_sync 槽);返回 asio::awaitable 即落入 co_reg 协程槽——同步/协程由签名决定,无需额外宏。 构建期 paozhu_pre 扫描 //@urlpath 自动生成 reg_raw 路由表,开发者不写任何注册语句。

Drogon —— 注册宏(HttpController + 反射,类名自动转路径):

class TestCtrl : public drogon::HttpController
{public:METHOD_LIST_BEGIN// 路径 "/testrand",GET 方法,绑定 testrand 方法ADD_METHOD_TO(TestCtrl::testrand, "/testrand", Get);METHOD_LIST_ENDvoid testrand(const HttpRequestPtr &req,std::function &&cb) const{auto resp = HttpResponse::newHttpResponse();resp->setBody("

随机数

");cb(resp); // 回调式返回} }; 方法挂在 HttpController 子类上,靠 DrObject 反射在 main 前静态自注册;类名 TestCtrl 默认映射为 /testctrl(可用 ADD_METHOD_TO 显式覆盖路径)。 同步/异步的选择落在单个方法形态上(回调 / std::future / CoroMapper/Task 协程),与 Paozhu「签名即槽位」不同。

一句话:Paozhu 是「在业务代码里写一行 //@urlpath 注解就完成注册」,回归式、零样板;Drogon 是「在类里用 METHOD_LIST_BEGIN/ADD_METHOD_TO 宏声明路由」,宏反射、需继承 HttpController 基类。

8. C++20 协程

维度 Paozhu Drogon(本仓) 支持 全栈协程:asio::awaitable + co_await + co_spawn(..., detached),遍布 server/router/httppeer/ORM/客户端 完整 C++20 协程:Task(coroutine.h:160)、AsyncTask、CallbackAwaiter、when_all、Mutex 协程锁 触发点 每连接 http1loop/http2loop 协程;路由 co_resolve;DB 查询 ORM CoroMapper;co_future/sync_wait 桥接 future 定位 协程是主范式(连 ORM 都是),回调极少 协程是可选范式,默认仍是回调 / future,协程为增强

关键差异:Paozhu 把协程作为唯一一等公民(IO、业务、DB 全链路 awaitable);Drogon 是「回调/future 为基础 + 协程叠加」的混合模型。Paozhu 用 Asio 的标准 co_await;Drogon 自研 Task/Awaiter 体系。

9. 多租户 / 主题

维度 Paozhu Drogon(本仓) 机制 站点 slot 模型:host_index → slot,_site_to_slot 每站点一条 slot,目录即命名空间(split_domain_path) 多 app / 多 controller 分组;依赖 DrObject 命名 主题 theme_view() 自动加 themes= 前缀;get_theme()(httppeer.h) 无内建 theme_view 等价机制(需自行实现)

Paozhu 的多租户是框架级内建(域名→slot→路由表隔离 + 主题视图),Drogon 本仓无对等的一站式多租户抽象。

10. 依赖与工程化

维度 Paozhu Drogon(本仓) 第三方核心 Asio(可切 boost::asio)、OpenSSL 3 trantor(自研)、OpenSSL / Botan(TLS 可插拔) 构建 CMake,构建期 codegen(paozhu_pre),controller/include 自动生成 CMake,drogon_ctl 外部 CLI 生成 附加模块 图片/Excel/Word/PDF/图表/SMS/支付 等 vendor 库 nosql_lib(Redis)、orm_lib 等 解析器 全自研(HTTP1/HTTP2/HPACK 无第三方) 自研 HTTP1 解析;drogon-master 无 HTTP/2 证书自动化(ACME / OCSP) 内置 ACME 客户端:acme_client.cpp + acme_run.cpp + acme_config.cpp 自动签发/续期,配合 acme.conf/ocsp_intv_time;OCSP stapling 由 ocsp_check.cpp 自动刷新装订 无内置 ACME 客户端(仅可在静态 location 配置 uri_prefix: /.well-known/acme-challenge/ 来提供 HTTP-01 挑战文件);lib/ 无 OCSP stapling 实现(BotanTLSProvider 中的 ocsp 仅是客户端校验证书链时接收的 OCSP 响应,非服务端装订);证书获取/续期依赖外部工具

10.1 收口:零第三方依赖的内置「全家桶」(Paozhu 工程化主线)

把前面各节的自研点收拢成一句话:Paozhu 的强制外部依赖极薄(约 Asio + OpenSSL 3),其上是几乎全栈自研;Drogon 核心同样薄(trantor + OpenSSL/Botan),但能力靠组合生态。

Paozhu 自研覆盖(不引入对应第三方库)

传输/应用协议:HTTP/1.1、HTTP/2(HPACK/ALPN/h2c)、WebSocket、MQTT(含 broker)、Redis 客户端、RPC、裸 Socket——线层/帧编解码全手写,无 libwebsockets / mosquitto / hiredis / 自研 RPC 框架等外部依赖。 数据库线层协议:MySQL(mysql_conn.cpp 的 caching_sha2_password 握手)、PostgreSQL(pg_conn.cpp 的 SCRAM-SHA-256)、SQLite——自研,不链 libmysqlclient/libpq/sqlite3(见 §4.8)。 证书自动化:内置 ACME 客户端 + OCSP stapling(acme_client.cpp/acme_run.cpp/ocsp_check.cpp),无需 certbot 等外部工具。 限流:连接接纳级过载保护自研(§5),非插件。 统一抽象:ws/mqtt/socket/redis 共用 subpub 注册内核(§6.6),是这套全家桶的串联线索。

Drogon 对照(薄核心 + 生态组合)

Redis 用 hiredis、DB 用 libmysqlclient/libpq/sqlite3、限流用 Hodor 插件、ACME 靠外部工具;不内置 MQTT/RPC/裸 Socket 服务器。 取舍:聚焦 HTTP Web 框架,把「协议全家桶」交给生态/外部库,换取成熟、文档完善、TLS 可插拔(Botan/OpenSSL)。

代价与收益同源:Paozhu 的「全家桶自研」带来部署简单(少装外部库)、能力一致、零协议授权/版本纠缠,但代码体量巨大、自研一切正确性自担、生态/社区小(见 §11);Drogon 则把「做透 HTTP + 易组合」作为工程主线。

11. 设计取舍总结

Paozhu 强项

零第三方依赖的内置「全家桶」:HTTP/2、MySQL/PG/SQLite 线层协议、WebSocket/MQTT(broker)/Redis/RPC/裸 Socket、ACME/OCSP、连接级限流——全部自研,仅强制依赖 Asio + OpenSSL;统一 subpub 内核串联多协议(收口 §2/§4.8/§5/§6/§10)。部署简单、能力一致、无协议库版本纠缠。 全栈 C++20 协程,异步心智模型统一(IO/业务/DB 都是 co_await)。 HTTP/2 内置(HPACK、ALPN、h2c),协议层自研且完整。 框架级多租户 + 主题视图。 ORM 从 SQL schema 文件生成,与运行库解耦,CI 友好。 构建期自动 codegen 强制刷新路由,避免「忘了注册」类 bug。 内置 ACME 自动签发/续期 + OCSP stapling(acme_client.cpp/acme_run.cpp/ocsp_check.cpp),证书自动化无需外部工具。

Paozhu 代价

单 io_context 共享 + 巨量方言 ORM 头文件(~450KB/方言),代码体量与维护面大。 生态/文档/社区远小于 drogon。 自研一切(解析、HPACK、路由),正确性需自担。

Drogon 强项

成熟工业级 reactor 模型(多独立 EventLoop、MPSC 跨线程、epoll/kqueue)。 反射宏注册 + 编译期静态自注册,零运行时扫描,注册路径自动推导。 完整 C++20 协程体系(Task/when_all/协程锁)。 生态丰富(CSP 模板、nosql、plugin、filter、ORM 三方言、示例齐全)。 社区活跃、文档完善、应用案例多。

Drogon 代价(drogon-master 实测)

drogon-master(lib/,drogon 1.9.14)无 HTTP/2 服务器实现(无 Http2Server/Http2Connection,HttpServer.cc 仅输出 HTTP/1.1)。drogon 主线截至 1.9.14 尚未内置 HTTP/2,paozhu 的 HTTP/2 是实际差异化能力。 handler 默认在 ioLoop 线程执行,重业务需手动 offload。 ORM 依赖连活库生成,离线/CI 需起库。

12. 选型建议

场景 建议 需要 HTTP/2(且所用 drogon 版本无 HTTP/2) Paozhu(内置 HTTP/2) 追求成熟生态、文档、社区、快速上手 Drogon 全链路协程异步、统一心智模型 Paozhu(协程为主范式) 多租户 SaaS / 主题化站点 Paozhu(框架内建) 离线 CI / 不依赖运行库生成 ORM Paozhu(SQL schema 生成) 经典 Reactor + 反射宏 + 编译期注册 Drogon 一站式零依赖全家桶(多协议 + 自管证书 + 自研 DB 协议,少装外部库) Paozhu

附:关键源码证据索引

Paozhu:vendor/httpserver/src/server.cpp、include/client_session.h:194、include/router.h:79、include/httppeer.h:41、src/http2_parse.cpp、vendor/httpcli/autopickmethod.cpp、autopickcontrolmethod.hpp:411、conf/server.conf。 Drogon(drogon-master):trantor/trantor/net/EventLoop.h、net/TcpServer.cc:65、net/inner/poller/EpollPoller.cc、lib/src/HttpRequestParser.cc、lib/src/HttpControllersRouter.cc:626、lib/inc/drogon/HttpController.h:31、orm_lib/inc/drogon/orm/Mapper.h:115、lib/inc/drogon/utils/coroutine.h:160、drogon_ctl/create_model.cc、drogon_ctl/create_view.cc。