上一篇 1.1 里我用五句话交代了"为什么选 Drogon",但那五句太像宣传册了。真到了面试或者技术评审上,一定会有人追问一句:"工业界到底谁在用 Drogon?"

这个问题很扎心,而且必须正面回答。这篇文章就干三件事:把 Drogon 和它的同类放到一张桌子上比,把"工业界实际用什么"讲清楚,最后交代我在这个项目上做这个选型时放弃了什么

一、先回答最扎心的那个问题:工业界真用 Drogon 吗?

诚实答案分三层。

第一层:大厂的 C++ 后端,几乎不使用"Web 框架"这个词。 你去问百度、腾讯、字节的 C++ 团队用什么,答案不会是 Drogon,也不会是 oat++,而是 RPC 框架——brpcSogou Workflow(及其上的 SRPC)、TARSgRPC。原因很简单:大厂内部服务之间走的是二进制协议 + 服务发现,HTTP 只是南北向入口那一层,而入口通常由 Nginx / OpenResty / Envoy / Go 网关承担,轮不到 C++ 业务服务直接对公网。

第二层:Drogon 是"社区里最好的 C++ Web 框架",不是"大厂标配"。 这两件事不矛盾。DrogonGitHub 上是 C++ Web 框架里星标最高的那一档,TechEmpower Round 19(2020-05)综合榜拿过第一,Round 21 综合分 7801 排在所有框架之首(同期 Rust actix 7667、ASP.NET Core 7077、Go gin 1943、Spring 1846)。但这只能证明它跑得快、功能全,不能证明大厂在生产用它。它的真实用户画像是:个人项目、创业公司、边缘/嵌入式 Linux 上的 HTTP 服务、以及需要 C++ 性能又不想自己拼装全家桶的小团队。

第三层:真正被生产验证的 C++ 通信框架,都有公开的落地清单。 举两个我查得到出处的:

框架 落地规模(公开资料)
brpc(百度开源,2022-12 从 Apache 孵化器毕业成 TLP 百度内部 4000+ 活跃模块、600 万+ 实例;小红书推荐 1 万+ 实例;爱奇艺广告 3000+ 台机器;vivo、滴滴、B站、作业帮、欢聚时代、第四范式等均有落地;Apache Doris / BaikalDB / StarRocks 用它做节点间 RPC
Sogou C++ Workflow 支撑搜狗几乎所有 C++ 后端在线服务(搜索、云输入法、在线广告),日处理请求百亿级;官方压测小包(64/512 B)可稳定在 50 万 QPS

注意这两位都不是"Web 框架":brpcRPC 框架,Workflow 是"通信 + 计算"一体的任务流引擎。它们的共同点是内部服务通信,不是"给你一个 HTTP 路由 + ORM + 模板引擎让你写业务"。

本质一句话C++ 在工业界的主战场是基础设施(存储、检索引擎、推荐、广告、音视频、游戏服务端),那里的关键词是 RPC 和延迟;而"用 C++ 写对外 HTTP 业务后端"本身就是小众选择,Drogon 是这个小众赛道里最好的那一个 🐾。

二、把赛道分清楚:Web 框架 / RPC 框架 / 网络库,别跨赛道比

技术选型文章最常见的错误,就是把 Drogonbrpc 拉到一起比"谁更强"。它们解决的不是同一个问题。先把赛道画出来:

C++ 服务端框架的三个赛道(越往上越靠近业务,越往下越靠近内核) Web 框架 对外 HTTP + 全家桶 Drogon ORM + Redis + WS HTTP/2 · 异步 oat++ 零依赖 · 体积小 Swagger 内置 userver Yandex 生产级 组件模型 + DB 驱动 Crow / Pistache 只要路由 轻量微框架 RPC 框架 服务间调用 二进制 + 治理 brpc 百度 600w+ 实例 bthread · bvar Sogou Workflow 搜狗全站 任务流 + 异步客户端 TARS 腾讯开源 含运营平台 gRPC C++ 跨语言通用 生态最广 网络库 只有事件循环 其余自己搭 Boost.Asio 事实标准 Beast 提供 HTTP muduo 陈硕 · one loop 教科书实现 libevent / libuv C 语言底层 跨语言生态 trantor Drogon 的底座 非阻塞网络库 本项目:阶段一~三用 Drogon(赛道一);阶段四服务拆分后的内部 RPC 用 brpc(赛道二)

把这张表压成文字版:

框架 赛道 并发模型 内置件 谁在生产用 最适合的场景
Drogon Web 全家桶 epoll/kqueue + 回调 / C++20 协程 ORMPG/MySQL/SQLite)、Redis 客户端、WebSocketHTTP/2Session、模板 社区、创业公司、边缘设备 想用 C++ 写完整 HTTP 服务,且不想拼装十个库
oat++ Web(零依赖) 自研异步 API ORM 模块化、Swagger/OpenAPIWebSocket IoT / 嵌入式 / 受限环境 二进制体积和依赖数量是硬约束
userver Web / 微服务 异步 + 组件模型 PG / Mongo / Redis / ClickHouse 驱动、完善的测试与可观测工具 Yandex 内部大量生产服务 C++ 微服务,且团队愿意接受它的组件范式
Crow Web 微框架 同步/半异步(无协程) 只有路由 + JSON 原型、教学 半天搭个 demo,别指望生产
Pistache Web 微框架 异步 路由 + REST 语义 少量自用 想要干净的 C++11 REST API
brpc RPC bthread(M:N 用户态线程) 多协议、bvar 监控、内置调试服务 百度 / 小红书 / 爱奇艺 / vivo / 滴滴 / B站… 内部服务间高性能调用,要治理要可观测
Sogou Workflow 通信+计算引擎 异步任务流 HTTP/Redis/MySQL/Kafka 异步客户端、DAG 调度 搜狗全站 一个进程里要编排复杂异步依赖
Boost.Asio / muduo 网络库 proactor / reactor 什么都没有 自研框架的地基 想自己造轮子、或者要极致可控

看清这张表,很多争论就没意义了:brpcDrogon 比"谁更适合写 HTTP 接口",就像拿变速箱和整车比谁更能跑。

三、那这个项目为什么选 Drogon:六条硬理由,每条都带代价

我给这个项目定的目标是"把一个秒杀系统从 400 QPS 一路优化到万级,并把每一层的取舍讲透"。在这个目标下,选型的判据不是"哪个最工业",而是**"哪个把我的精力留在架构上,而不是消耗在胶水代码上"**。

理由一:它把地基活一次性干完了。 HTTP 服务、ORMRedis 客户端、WebSocketSessionJSON 全内置。换成 CrowPistache,我得自己接 MySQL 客户端、自己包 hiredis、自己写连接池——主线会从我真正想讲的"行锁竞争 / 缓存一致性 / MQ 削峰"漂移成"如何集成七个第三方库"。

代价:全家桶意味着框架偏"重",它要拥有 main() 和事件循环,想和别的框架共存很难(不像 PocoPistache 那样可以只取一部分用)。

理由二:异步是内核级的,不是外挂的。 Drogon 的网络层是自带的 trantor:每个 IO 线程一个 EventLoop,连接 accept 后绑定到某个 EventLoop,后续读写回调都在那一个线程内执行——天然免锁。数据库和 Redis 客户端同样异步,回调统一派发回 EventLoop 线程。

这一条对本项目是决定性的。配置里 threads_num=4,也就是只有 4 个 IO 线程;如果 MySQL 客户端是同步的,一次 DB 往返就会把该线程上排队的请求全部卡住,4 线程直接变成 4 并发。实测阶段一干净态基线约 440 QPS,排查下来瓶颈完全在 MySQL 行锁串行化上(每个请求占一条连接、并持 seckill_sku 行锁直到事务提交),框架层没有成为瓶颈——这正是我要的:让瓶颈暴露在架构层,而不是被框架的实现方式掩盖。

理由三:内置 ORM 的异步事务语义。 秒杀下单的"扣库存 + 落订单"必须在同一个事务里,DrogonTransaction 给了这个能力。下面是项目里真实在跑的写法(Drogon 1.9.10 回调式,节选):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// src/service/SeckillService.cc(节选,省略异常回调与日志)
db_->newTransactionAsync(
[this, userId, skuId, cb = std::move(callback), token](
const std::shared_ptr<drogon::orm::Transaction> &tx) {
if (!tx) { cb(false, "DB_ERROR: no transaction (timeout)"); return; }

// v1.9.10 的坑:Transaction 根本没有 commit() 成员。
// 提交发生在“最后一个 shared_ptr<Transaction> 析构”时,成功才回调这里;
// rollback() 会置标志位,析构便不再自动提交。
tx->setCommitCallback([cb, token, this, skuId](bool committed) {
if (committed) {
if (cache_) cache_->invalidateOnOrder(skuId); // 提交后才删缓存
cb(true, "OK");
} else {
cb(false, "DB_ERROR: commit failed");
}
});

// 步骤 1:单行原子扣减。affectedRows==0 即已售罄——绝不先 SELECT 再 UPDATE
tx->execSqlAsync(
"UPDATE seckill_sku SET stock = stock - 1 WHERE id = ? AND stock > 0",
[tx, userId, skuId, cb, token](const drogon::orm::Result &result) {
if (result.affectedRows() == 0) {
tx->rollback(); // 售罄:显式回滚
cb(false, "SOLD_OUT");
return;
}
// 步骤 2:同事务内落订单。用 ON DUPLICATE KEY 而非裸 INSERT:
// 裸 INSERT 撞上 uk_user_sku 唯一键会抛异常,被当成 DB 错误回滚,
// 而它本该是“重复下单”的业务拒绝。
tx->execSqlAsync(
"INSERT INTO seckill_order (user_id, sku_id, status, create_time) "
"VALUES (?, ?, 1, NOW()) ON DUPLICATE KEY UPDATE id = id",
[tx, cb, token](const drogon::orm::Result &r2) {
if (r2.affectedRows() == 0) { // 命中唯一键 = 重复下单
tx->rollback(); // 把步骤 1 多扣的库存还回去
cb(false, "DUPLICATE_ORDER");
return;
}
// 成功路径不手动提交:本 lambda 返回后 tx 的拷贝相继析构
// → 自动 commit → setCommitCallback → cb(true, "OK")
},
/* 异常回调:rollback + 记日志 + cb(DB_ERROR) */,
userId, skuId);
},
/* 异常回调:rollback + 记日志 + cb(DB_ERROR) */,
skuId);
});

Crow 这类"只有路由"的框架,这套异步事务语义得自己基于 MySQL C API 实现一遍。

顺带说一句:token(4.8 应用层在途闸门的标记)被按值捕获进每一层 lambda,这不是啰嗦——只捕获进最外层的话,最外层 lambda 发起第一条 SQL 后就析构了,标记会被提前清掉,闸门形同虚设。异步框架里的资源生命周期,全靠 shared_ptr 的析构时机来兜。

理由四:内置 Redis 客户端是异步的,这一点在登录模块救了命。 我最初规划的是 redis-plus-plus,落地时换成了 Drogon 内置的 nosql::RedisClient(底层 hiredis)。原因很硬:redis-plus-plus同步 API,而我们的 handler 跑在 IO 线程上,同步等一次 Redis 往返会阻塞整个线程上的排队请求——这是结构性缺陷,不是调优能解决的。内置客户端异步、与事件循环同构,且零额外依赖(只需 libhiredis-dev)。

理由五:性能天花板足够高,不会反过来成为我要优化的对象。 TechEmpower Round 19 综合榜第一;Round 21 综合分 7801 居首;Fortunes 场景(带 ORM + 模板渲染,最接近真实业务)drogon-core 长期在百万 rps 量级;官方口径是 Ryzen 3700X 单核 > 15 万 req/s。我要练的是架构优化,不希望中途发现"框架本身就是天花板"。

理由六:工程约束——两条 cmake 命令能构建。 这个项目要跟着文章一起发布,读者能复现是硬指标。drogon_ctl 一键生成脚手架,依赖用 apt 装齐,cmake .. && make 就完事。如果为了引入某个库需要 FetchContentGitHub 拉源码(我在 JWT 选型上正是因此放弃 jwt-cpp、改为自实现 HS256),在 WSL 网络环境下就多一个损坏点。

最关键的代价Drogon 不提供任何服务治理件——没有分布式锁、没有限流、没有注册中心、没有熔断。Java 那边 Redisson / Sentinel / Nacos 开箱即用,这里全部得自己写。但对这个项目来说这不是缺点而是设计意图:阶段四"自实现治理"本来就是我要练的东西。反过来说,如果你要做的是交付周期紧张的生产系统,这条会直接劝退。

四、诚实地讲 Drogon 的短板

选型论证如果只讲优点,就是广告。下面这些是真实的代价,我也确实被它们绊过:

短板 具体表现 我在项目里怎么应对
生态薄 C++ Web 框架整体都是小众赛道,Drogon 已是星标最高的那档,但和 Spring / Gin 完全不是一个量级;第三方中间件集成要自己写 只依赖框架核心能力,业务逻辑尽量不绑框架特性
治理件为零 分布式锁 / 限流 / 注册配置 / 熔断,一个都没有 全部自实现(阶段四主线),Redis SET NX + Lua 打底
回调与协程双模式并存 历史包袱:早期 API 是回调式,新版加协程。1.9.10 的 Transactioncommit() 成员都没有——提交靠"最后一个 shared_ptr<Transaction> 析构"自动触发,rollback() 置标志阻止自动提交;且流式 << / >> 不支持 (Result, exception_ptr) 合并回调,每条 SQL 只能用 execSqlAsync(sql, 成功回调, 异常回调, 参数...)。开 C++20 协程的新版才回到 co_await 形态 钉死 Drogon 1.9.x 回调式写法(见项目 ADR-5),升级协程版时统一改写
文档与社区响应 中文资料相对多,但深度问题基本靠读源码 关键实现(如取客户端 IP)直接翻 HttpRequest.h 确认
单机框架 不带服务发现、配置中心、链路追踪 阶段四用 brpc + etcd
招人难 团队里没人会就得自己扛,且 C++ 开发者价格高、供给少 这个项目的定位是个人技术实践,不是团队协作产品

其中第三和第四条我都踩过实坑。举个具体的:Drogon 1.9.10 的 HttpRequest 只有 getPeerAddr(),没有 getClientIp() 也没有 getRealIp()——框架不内建"从 X-Forwarded-For 解析真实客户端 IP"的能力。我按直觉写了 req->getClientIp(),编译直接挂,翻官方头文件才确认得自己解析请求头。这是小众框架最典型的风险:没有足够的博客告诉你坑在哪,你得自己去读源码。

五、那"实际工作中该用什么框架"?

这才是问题里最有价值的一半。我的判断规则很简单——先看你在写什么,再看 C++ 是不是这个场景的正确语言

C++ 服务端选型决策:先问清场景,再谈框架 你在写"业务 CRUD / 中台"吗? 订单、用户、后台管理、报表 别用 C++ Go(gin/go-zero) / Java(Spring) 继续往下 确实需要 C++ 是"服务间调用"还是"对外 HTTP"? 南北向入口 vs 东西向通信 服务间 → RPC 框架 brpc / Workflow / TARS 对外 HTTP → Web 框架 Drogon / oat++ / userver 二进制体积 / 依赖数量敏感吗? 嵌入式、容器镜像、离线交付 是 → oat++ 零依赖,sub-MB 二进制 否 → Drogon 要 ORM + Redis + WS 团队有 infra 能力且要极致可控? 自研网关、专用协议、定制调度 是 → Boost.Asio / muduo 自研(或在其上封装) 代价:所有上层能力自己维护,只有大厂 infra 团队扛得住

对应到具体岗位和场景:

场景 该用什么 为什么
电商 / 金融 / 中后台业务系统 JavaSpring Boot + 全家桶) 中间件生态 100% 覆盖、招人容易、十年可维护
云原生基础设施、API 网关、IM 长连接 Gogin / go-zero / Kratos goroutine 模型 + 单二进制部署,人效与性能的甜点
存储引擎、检索引擎、推荐 / 广告、特征服务 C++ + brpc / Workflow / TARS 延迟与内存可控,且有工业级验证(brpc 在百度 600 万+ 实例)
游戏服务端 / 音视频 / 高频交易 C++ + 自研或 Asio 生态 GC 停顿不可接受,需要精细控制内存布局
嵌入式 / IoT 设备上的 HTTP 服务 oat++ 零外部依赖、二进制体积可压到 MB 以下
需要 C++ 性能、又要快速出 HTTP 服务的中小项目 Drogon 全家桶最全、性能天花板最高、社区最大
学习 / 面试用的练手项目 Drogon(想讲架构)或 muduo 自研(想讲底层) 前者让你专注架构,后者让你吃透 epoll

六、如果有人问你"工业界没人用 Drogon",怎么答

这是我给自己的标准答案,三层递进:

  1. 承认事实:对,大厂 C++ 后端的主力是 brpc / Workflow / TARS 这类 RPC 框架,C++HTTP 业务后端本身就是小众场景,Drogon 不是大厂标配。
  2. 说清定位Drogon 是"社区最好的 C++ Web 框架"——TechEmpower 综合榜拿过第一,功能完整度(ORM + Redis + WebSocket + HTTP/2)在同类里没有对手。用它不等于宣称它是工业标准。
  3. 回到目标:这个项目的目标是"把高并发架构的每一层优化讲透",不是"交付一个生产级电商系统"。DrogonHTTP / ORM / Redis 的地基活干完,把架构层的取舍完整地留给我——实测基线 440 QPS 的瓶颈在 MySQL 行锁,不在框架,后续每一级跃迁(缓存 → MQLua 原子预扣)也都是架构收益,没有一级来自换框架。这恰恰证明选型是对的:我把力气花在了该花的地方。

本质一句话:选框架不是选"最强的那个",而是选**"把你的瓶颈留在你想练的地方"的那个**。Drogon 让我把 90% 的精力花在行锁、缓存一致性、削峰、原子扣减上,只有 10% 花在框架本身——这就是我选它的全部理由 🐾。

七、本项目里的实际取舍清单

最后把落到代码上的决策一次列清,方便对照源码:

决策 选择 放弃的
Redis 客户端 Drogon 内置 nosql::RedisClient(异步,hiredis 底层) redis-plus-plus(同步,会阻塞 IO 线程)
JWT 签发 自实现 HS256(OpenSSL HMAC + base64url jwt-cpp(需 FetchContentGitHub
ORM / 事务 Drogon 内置 DbClient 异步事务 mysql-connector-cpp / SOCI
短信发送 自签发 / 日志模式,不接网关 腾讯云 API + libcurl + TC3 签名
版本与写法 Drogon 1.9.x 回调式 ORM API C++20 协程版(API 语义会变,见 ADR-5
服务治理 全部自实现(Lua 原子限流 / 令牌桶 / 布隆) 任何第三方治理库
前端 阶段四之前不做(ADR-4 Vue / Vite 构建链