提到Web开发,大多数人首先想到的是Python、Java或者JavaScript这些"网红语言"。但如果你正在开发一个需要极致性能的在线交易系统,或者一个资源受限的嵌入式Web服务,C++可能是更好的选择。我见过太多项目因为初期选型不当,后期不得不推倒重来的案例。
C++ Web框架的优势主要体现在三个方面:首先是性能,在TechEmpower的基准测试中,C++框架经常霸榜;其次是资源控制,你可以精确管理每个字节的内存;最后是跨平台能力,同一套代码可以跑在树莓派和服务器集群上。去年我们团队用Drogon重构了一个Python服务,QPS直接从2000提升到15000,这就是C++的魔力。
当然,C++开发Web也有痛点。内存安全问题让人头疼,开发效率比不上脚本语言,生态工具也不够丰富。但现代C++标准(C++17/20)和这些新兴框架正在改变现状。比如cinatra的异步IO实现,让代码既高效又简洁。
Drogon是我最推荐的全能型选手。它基于事件循环模型,内置ORM和模板引擎,连WebSocket都支持。记得第一次用它的异步接口时,我写了这样的代码:
cpp复制app.registerHandler("/api/user",
[](const HttpRequestPtr& req,
std::function<void(const HttpResponsePtr&)>&& callback) {
auto client = app().getDbClient();
client->execSqlAsync("SELECT * FROM users",
[callback](const Result& r){
Json::Value ret;
// 处理结果
auto resp = HttpResponse::newHttpJsonResponse(ret);
callback(resp);
});
});
这种非阻塞式编程模式,让单机轻松支撑数万并发。而Facebook的Proxygen更像是个"瑞士军刀",它的HTTP/2实现堪称教科书级别。不过要注意,Proxygen的文档比较晦涩,我花了三天才搞明白它的响应式编程模型。
当需要快速原型开发时,我会选择Oat++。它的DTO(Data Transfer Object)系统让我眼前一亮:
cpp复制ENDPOINT("GET", "/users/{id}", getUserById,
PATH(Int64, id)) {
auto user = User::loadFromDb(id);
return createDtoResponse(Status::CODE_200, user);
}
这种声明式API比REST SDK的模板地狱清爽多了。至于Crow,它的路由语法简直是对Flask的复刻:
cpp复制CROW_ROUTE(app, "/hello")([](){
return "Hello world";
});
但要注意,Crow的维护不太活跃,上次更新还是一年前。如果要做长期项目,建议考虑其他方案。
在对接Azure云服务时,C++ REST SDK是我的不二之选。它的OAuth2支持开箱即用:
cpp复制http_client_config config;
config.set_credentials(oauth2_config(
"client_id", "client_secret",
"https://login.microsoftonline.com/common/oauth2/token",
"https://graph.microsoft.com/"));
auto client = http_client(U("https://graph.microsoft.com/v1.0/me"), config);
client.request(methods::GET).then([](http_response response){
// 处理响应
});
不过它的异步模型基于PPLX,学习曲线略陡。有次我忘了调用wait(),调试了整整一天才发现请求根本没发出去。
给智能摄像头开发Web接口时,Civetweb帮了大忙。它的内存占用不到1MB,还能通过Lua扩展功能:
cpp复制mg_set_option(server, "document_root", "/var/www");
mg_set_option(server, "listening_port", "8080");
mg_start_server(server, [](mg_connection* conn, int ev, void* p) {
if (ev == MG_EV_HTTP_REQUEST) {
mg_send_head(conn, 200, json.size(), "Content-Type: application/json");
mg_write(conn, json.c_str(), json.size());
}
});
而Pistache的REST实现更规范,适合需要OpenAPI支持的场景。它的路由系统支持参数校验:
cpp复制Routes::Get(router, "/users/:id",
Routes::bind(&UserController::getUser, this));
当产品经理要求三天做出一个数据看板时,Wt救了我的命。它的Widget系统让我完全不用写JS:
cpp复制auto container = std::make_unique<Wt::WContainerWidget>();
auto chart = container->addNew<Wt::Chart::WCartesianChart>();
chart->setModel(dataModel);
chart->setXAxisColumn(0);
chart->addSeries<Wt::Chart::WLineSeries>(1);
但要注意,Wt的页面渲染是服务端完成的,频繁交互时延迟明显。我们后来改用WebSocket才解决性能问题。
cinatra可能是语法最优雅的框架,它的coroutine支持让异步代码像同步一样简单:
cpp复制app.set_http_handler<GET>("/users", [](request& req, response& res) {
auto users = co_await db.query("SELECT * FROM users");
res.set_status_and_content(200, json(users));
});
不过它要求C++17,在一些老旧系统上编译会遇到麻烦。我在CentOS 7上折腾了半天才搞定toolchain。
面对具体项目时,我通常这样决策:
最近帮朋友选型时,我们做了个对比测试:在4核8G的机器上,Drogon的JSON API能达到12万QPS,而Python的FastAPI只有2万。当然,开发效率是另一回事——同样的功能,Python版只用了2天,C++版花了1周。
最后给个实用建议:先用Oat++或Crow做原型验证,确认业务逻辑可行后,再用Drogon重构性能关键路径。我们团队现在80%的Web服务都跑在Drogon上,只有管理后台这种低频应用还在用Python。