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 模型(核心差异)
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 走编译期反射 + 宏 + 静态自注册;Paozhu 走源码注解 + 构建期 codegen 生成 (见 §3.4),Drogon 无对等机制。 Paozhu( 扫描): Drogon( 宏,编译期): Paozhu:核心 ,rpc/同步链跳过重复精确查找)。 Drogon: ): Paozhu —— 每条路由拆成 4 个独立槽位,同步与协程是「路由层一等公民」: 一并停用)。 Drogon:pre 逻辑由 Filters( ),没有「一条路由同时声明同步 pre + 协程 handler」这种按入口拆分的混编槽位。 结论:是的,Paozhu 在 实现「单请求内多 handler 顺序编排」。 这意味着一个请求可以「handler A → 压 B、C、D → A 返回空串后依次跑 B→C→D」,构成轻量顺序工作流 / pipeline。Drogon 没有对等的内建机制 :它的组合原语是 Filters/Middlewares(横切关注,非顺序步骤编排)与 handler 自身逻辑;若要在 Drogon 里串多个 handler,需 handler 内手动调用或重定向,框架层不提供编排栈。 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 隔离。 核心差异:Drogon 的路由是「宏反射 + 静态注册 + Filters/Middlewares」的工业级经典范式,关注点分离干净;Paozhu 是「注解 + codegen + 站点 slot + 编排」的一体化范式,额外给了请求内顺序任务编排这一轻量工作流能力,且多租户在路由层即内建。 二者均支持 MySQL / PostgreSQL / SQLite 三方言,且 ORM 查询都支持 C++20 协程异步。但在生成时机、代码组织、查询风格、缓存、事务、关系表达 上差异显著。 Paozhu( : 风格差异: 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) 与查询,分页导航(总页数/窗口)要自己算。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/streamHTTP/2
内置:
http2_parse.cpp(192KB) + http2_frame/huffman/ring_queue/send_queue,含 HPACK、ALPN、h2c,http2_enable=1drogon-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 请求模型是 httppeer3. 路由系统(深度)
reg_raw表。此外 Paozhu 额外内置了基于 httppeer::flow_method的简单任务编排3.1 注册方式与代码生成
controller/src/*.cpp 写 //@urlpath(pre,urlpath):mask,构建期 paozhu_precommon/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 等辅助表。HttpController.hMETHOD_ADD(method, pattern, ...) / ADD_METHOD_TO(method, path, ...)(HttpController.h:31-34)→ registerMethod(&method, pattern, {...}, ...);还支持 ADD_METHOD_VIA_REGEX(:35)写正则。class methodRegistrator + static methodRegistrator registrator_(HttpController.h:127-139),构造函数里把方法名 + 参数包登记进框架——在 main之前由 CRT 静态初始化完成,零运行时扫描。类名到路径的映射由 DrClassMap::demangle(typeid(T).name()) 自动推导(HttpBinder.h:235),:: 转 /。3.2 匹配结构
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-299router.cpp 两级查表:lookup_exact → lookup_registered → lookup_key_in_slots,router.cpp:180-185),未注册的跳转名在编排排空时被跳过(router.cpp:350-363)。HttpControllersRouter + StaticFileRouter。三套结构(HttpControllersRouter.ccsimpleCtrlMap_:无参数精确匹配(小写 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)与同步/协程混编
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_clientrunpre_kind/reg_kind 自动分流,开发者无需自己切线程或包 std::async。这与 Drogon「handler 选一种形态、CoroMapper 是另一个类型」的设计思想不同——Paozhu 把「是否协程」下沉为每个注册入口的属性,而非整条 handler 的类型。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 条而非整条丢弃(避免鉴权静默失效)。pop_flow_index),若驱动是同步链却弹到协程条目则停止排空(:364-368),协程驱动则就地 co_await 继续(:865-873)。HttpFilter + ADD_FILTER_TO / GlobalFilters.cc)与 Middlewares(HttpMiddleware,MiddlewaresFunction.cc + HttpAppFramework.h:718)承载,框架在 handler 前按 path/全局顺序执行,与路由表解耦、可复用、可排序。同步/异步的选择落在单个方法形态上(binder 的 Blocking/Callback/future 三态,或协程用独立的 CoroMapper3.4 简单任务编排(Paozhu
flow_method)—— 是,Paozhu 支持
httppeer上内置了简单任务编排,通过 httppeer::flow_methodhttppeer 持有一个懒分配的栈 std::unique_ptr> flow_method(httppeer.h:300-301)——存的是后续要执行的 handler 名。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)。
3.5 多租户 / 站点 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 hooksFilters + 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)无;业务层分组
flow_method4. ORM 深度对比
4.1 生成代码结构
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)。
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。