Drogon: 一个开源的C++高性能Web框架

目录

1.简介

2.安装方法

3.快速入门示例

3.1.创建简单的Web服务

3.2.控制器类路由

3.3.数据库集成与ORM

3.4.过滤器-实现AOP切面编程

[3.5.构建RESTful API](#3.5.构建RESTful API)

4.完整功能示例

5.性能与适用场景

6.总结

学习资源


1.简介

Drogon是一个基于C++14/17/20编写的高性能HTTP Web应用框架 ,它的设计目标是充分利用 C++ 的性能优势,同时简化 Web 开发流程,适合构建高并发、低延迟的后端服务。它采用异步非阻塞 的架构设计,使用epoll(Linux)和kqueue(macOS/FreeBSD)等高效事件处理机制,即使在大量并发请求下也能保持出色的性能表现。Drogon是跨平台的,支持Linux、macOS、FreeBSD、OpenBSD和Windows等多种操作系统。

它的核心特性有:

1.高性能异步架构: 基于事件驱动模型(依赖 epoll/kqueue 等 IO 复用机制),支持异步非阻塞 IO,能高效处理大量并发连接,性能接近原生 C 开发的服务。

2.全面的 HTTP 支持:

  • 支持 HTTP/1.0、HTTP/1.1,以及 HTTPS(基于 OpenSSL);
  • 内置 RESTful API 路由系统,支持多种路由定义方式(函数、lambda、控制器类);
  • 支持请求参数解析、Cookie/Session 管理、文件上传下载等。

3.**WebSocket 支持:**原生支持 WebSocket 协议,可轻松实现实时通信(如聊天、实时数据推送),提供回调接口处理连接建立、消息收发、断开等事件。

4.**ORM 功能:**内置轻量级 ORM,支持 MySQL、PostgreSQL、SQLite 等数据库,可通过 C++ 类映射数据库表,简化 CRUD 操作(无需手写 SQL)。

5.**灵活路由系统:**支持RESTful风格路由设计,可轻松定义各种HTTP端点

6.**视图渲染:**支持后端渲染,通过CSP(类JSP)模板生成动态HTML页面

7.**JSON处理:**内置JSON序列化与反序列化,非常适合开发RESTful API

8.**过滤器机制:**提供AOP支持,可轻松实现登录验证、日志记录等统一逻辑

9.**灵活的扩展:**支持插件机制,可自定义中间件(如日志、权限校验)、过滤器等,满足复杂业务需求。

10.**跨平台:**支持 Linux、macOS、Windows 等主流操作系统,依赖 C++17 及以上标准(需现代编译器如 GCC 7+、Clang 5+、MSVC 2017+)。

2.安装方法

Drogon 依赖一些基础库(如 libuuidopenssljsoncpp 等),安装前需先配置依赖,再通过源码编译安装。

Linux/macOS 示例(以 Ubuntu 为例)

1.安装依赖:

cpp 复制代码
sudo apt-get install g++ cmake git libssl-dev libjsoncpp-dev libuuid-dev zlib1g-dev

2.克隆源码并编译:

cpp 复制代码
git clone https://github.com/drogonframework/drogon.git
cd drogon
git submodule update --init  # 拉取子模块(如 trantor 事件库)
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local  # 指定安装路径
make -j4  # 并行编译
sudo make install

在其他操作系统上,你也可以通过包管理器安装,例如在Debian上可以使用apt-get install libdrogon-dev

3.快速入门示例

3.1.创建简单的Web服务

下面是一个简单的Drogon应用示例,它创建了一个返回JSON响应的HTTP服务:

cpp 复制代码
#include <drogon/drogon.h>
using namespace drogon;

int main() {
    // 设置HTTP路由
    app().registerHandler(
        "/hello",
        [](const HttpRequestPtr& req,
           std::function<void(const HttpResponsePtr&)>&& callback) {
            // 创建JSON响应
            Json::Value json;
            json["message"] = "Hello, Drogon!";
            json["status"] = "success";

            auto resp = HttpResponse::newHttpJsonResponse(json);
            callback(resp);
        },
        {Get});

    // 配置并启动服务器
    app().setLogPath("./")
          .setLogLevel(trantor::Logger::kWarn)
          .addListener("0.0.0.0", 8080)
          .setThreadNum(16)
          .run();

    return 0;
}

这个简单示例展示了Drogon的基本用法:

  • 注册一个处理函数到路径/hello
  • 当收到GET请求时,返回一个JSON响应
  • 服务器监听在8080端口,使用16个处理线程

编译命令(假设文件名为 main.cpp):

cpp 复制代码
g++ main.cpp -o app -ldrogon -std=c++17

运行后访问 http://localhost:8080/hello 即可看到结果。

3.2.控制器类路由

对于复杂业务,推荐使用控制器类管理路由,通过宏定义简化配置。

cpp 复制代码
#include <drogon/HttpController.h>
using namespace drogon;

// 定义控制器类
class UserController : public HttpController<UserController> {
public:
    // 路由映射:GET /user/{id}
    METHOD_LIST_BEGIN
        // 绑定 "/user/{id}" 到 getUser 方法,支持 GET
        ADD_METHOD_TO(UserController::getUser, "/user/{id}", Get);
    METHOD_LIST_END

    // 处理函数:参数从路由中提取({id} 对应 path 中的 id)
    void getUser(const HttpRequestPtr& req,
                 std::function<void(const HttpResponsePtr&)>&& callback,
                 int id) {
        auto resp = HttpResponse::newHttpResponse();
        resp->setBody("User ID: " + std::to_string(id));
        callback(resp);
    }
};

int main() {
    // 注册控制器(Drogon 会自动解析路由)
    drogon::app().registerController(std::make_shared<UserController>());
    drogon::app().run();
    return 0;
}

访问 http://localhost:8080/user/123 会返回 User ID: 123

3.3.数据库集成与ORM

Drogon内置了ORM支持,可以轻松操作数据库。以下是一个使用ORM的示例:

假设数据库中有 users 表(字段:id INT, name VARCHAR(50), age INT),用 Drogon ORM 操作:

1.定义数据模型(映射表结构):

cpp 复制代码
#include <drogon/orm/DbModel.h>
using namespace drogon::orm;

// 映射 users 表
class User : public DbModel<User> {
public:
    // 定义字段(与表字段对应)
    Field<int> id;
    Field<std::string> name;
    Field<int> age;

    // 绑定表名和主键
    TABLE_NAME("users")
    PRIMARY_KEY(id)
};

2.数据库操作(查询 / 插入):

cpp 复制代码
#include <drogon/drogon.h>
#include "User.h"  // 引入模型

int main() {
    // 配置数据库连接(MySQL 示例)
    drogon::app().addDbClient("mysql", "host=localhost port=3306 dbname=test user=root password=123456");

    // 获取数据库客户端
    auto client = drogon::app().getDbClient();

    // 插入一条记录
    client->insert<User>(User{}
        .setName("Alice")
        .setAge(25))
        .then([](const Result& res) {
            std::cout << "Inserted ID: " << res.lastInsertId() << std::endl;
        })
        .catchError([](const DrogonDbException& e) {
            std::cerr << "Insert error: " << e.what() << std::endl;
        });

    // 查询 age > 20 的用户
    client->query<User>(Select(User::name, User::age).where(User::age > 20))
        .then([](const std::vector<User>& users) {
            for (const auto& u : users) {
                std::cout << "Name: " << u.name << ", Age: " << u.age << std::endl;
            }
        })
        .catchError([](const DrogonDbException& e) {
            std::cerr << "Query error: " << e.what() << std::endl;
        });

    drogon::app().run();
    return 0;
}

3.4.过滤器-实现AOP切面编程

Drogon的过滤器功能允许你在请求到达处理器之前执行通用逻辑,比如身份验证:

cpp 复制代码
class LoginFilter : public drogon::HttpFilter<LoginFilter>
{
public:
    virtual void doFilter(const HttpRequestPtr &req,
                          FilterCallback &&fcb,
                          FilterChainCallback &&fccb) override
    {
        // 检查用户是否登录
        auto session = req->getSession();
        if (session->find("user")) {
            // 用户已登录,继续处理
            fccb();
        } else {
            // 用户未登录,返回错误
            Json::Value json;
            json["error"] = "Unauthorized";
            json["message"] = "Please login first";

            auto resp = HttpResponse::newHttpJsonResponse(json);
            resp->setStatusCode(k401Unauthorized);
            fcb(resp);
        }
    }
};

然后在控制器中注册过滤器:

cpp 复制代码
PATH_LIST_BEGIN
PATH_ADD("/profile", Get, "LoginFilter");
PATH_LIST_END

3.5.构建RESTful API

结合上述功能,我们可以构建一个完整的RESTful API:

cpp 复制代码
#include <drogon/drogon.h>

using namespace drogon;

class UserAPI : public HttpSimpleController<UserAPI>
{
public:
    void asyncHandleHttpRequest(const HttpRequestPtr& req,
                               std::function<void(const HttpResponsePtr&)>&& callback) override
    {
        auto method = req->getMethod();
        Json::Value response;

        if (method == Get) {
            // 获取用户列表
            response["users"] = Json::arrayValue;
            response["count"] = 0;
        } else if (method == Post) {
            // 创建新用户
            auto json = req->getJsonObject();
            if (json) {
                response["status"] = "user created";
                response["id"] = 1001;
            } else {
                response["error"] = "invalid input";
            }
        } else if (method == Put) {
            // 更新用户
            response["status"] = "user updated";
        } else if (method == Delete) {
            // 删除用户
            response["status"] = "user deleted";
        }

        auto resp = HttpResponse::newHttpJsonResponse(response);
        callback(resp);
    }

    PATH_LIST_BEGIN
    PATH_ADD("/api/users", Get, Post, Put, Delete);
    PATH_LIST_END
};

配置文件

Drogon支持通过JSON文件配置应用:

config.json:

cpp 复制代码
{
  "app": {
    "log_path": "./",
    "log_level": "WARN"
  },
  "listeners": [
    {
      "address": "0.0.0.0",
      "port": 8080,
      "https": false
    }
  ],
  "db_clients": [
    {
      "name": "default",
      "type": "postgresql",
      "host": "localhost",
      "port": 5432,
      "dbname": "mydatabase",
      "user": "myuser",
      "passwd": "mypassword"
    }
  ],
  "thread_num": 16
}

在代码中加载配置:

cpp 复制代码
app().loadConfigFile("../config.json");
app().run();

4.完整功能示例

下面提供一个使用 Drogon 实现的简单 Web 应用示例,包含首页展示带参数的用户信息页表单提交与处理三个核心功能,覆盖了 Drogon 的路由配置、控制器使用、HTTP 请求处理等基础用法。

示例功能说明

  1. 访问 GET /:显示首页(HTML 页面),包含导航链接。
  2. 访问 GET /user/{id}:通过用户 ID 查询模拟的用户信息(使用控制器类实现)。
  3. 访问 GET /form:显示一个表单页面,支持提交用户名和邮箱。
  4. 提交表单到 POST /submit:处理表单数据并返回结果。

完整代码(main.cpp

cpp 复制代码
#include <drogon/drogon.h>
#include <drogon/HttpController.h>
#include <string>

using namespace drogon;
using namespace std;


// 1. 首页路由处理(直接通过handler注册)
void handleHome(const HttpRequestPtr& req, function<void(const HttpResponsePtr&)>&& callback) {
    // 构建HTML首页内容
    string html = R"(
        <html>
        <head><title>Drogon 示例应用</title></head>
        <body>
            <h1>欢迎使用 Drogon 示例应用</h1>
            <ul>
                <li><a href="/user/1">查看用户1的信息</a></li>
                <li><a href="/user/2">查看用户2的信息</a></li>
                <li><a href="/form">提交表单</a></li>
            </ul>
        </body>
        </html>
    )";
    auto resp = HttpResponse::newHttpResponse();
    resp->setBody(html);
    resp->setContentTypeCode(CT_TEXT_HTML); // 声明为HTML类型
    callback(resp);
}


// 2. 用户信息控制器(处理带参数的路由)
class UserController : public HttpController<UserController> {
public:
    // 路由映射:GET /user/{id}
    METHOD_LIST_BEGIN
        ADD_METHOD_TO(UserController::getUserInfo, "/user/{id}", Get);
    METHOD_LIST_END

    // 处理用户信息请求(从路由参数中获取id)
    void getUserInfo(const HttpRequestPtr& req,
                     function<void(const HttpResponsePtr&)>&& callback,
                     int userId) {
        // 模拟用户数据(实际项目中可能从数据库查询)
        string userName;
        string userEmail;
        if (userId == 1) {
            userName = "张三";
            userEmail = "zhangsan@example.com";
        } else if (userId == 2) {
            userName = "李四";
            userEmail = "lisi@example.com";
        } else {
            userName = "未知用户";
            userEmail = "未知邮箱";
        }

        // 构建用户信息HTML
        string html = fmt::format(R"(
            <html>
            <head><title>用户信息</title></head>
            <body>
                <h1>用户 {} 的信息</h1>
                <p>ID: {}</p>
                <p>姓名: {}</p>
                <p>邮箱: {}</p>
                <a href="/">返回首页</a>
            </body>
            </html>
        )", userId, userId, userName, userEmail);

        auto resp = HttpResponse::newHttpResponse();
        resp->setBody(html);
        resp->setContentTypeCode(CT_TEXT_HTML);
        callback(resp);
    }
};


// 3. 表单处理相关路由
// 3.1 显示表单页面
void showForm(const HttpRequestPtr& req, function<void(const HttpResponsePtr&)>&& callback) {
    string html = R"(
        <html>
        <head><title>提交表单</title></head>
        <body>
            <h1>用户表单</h1>
            <form action="/submit" method="post">
                <label>用户名: <input type="text" name="username" required></label><br>
                <label>邮箱: <input type="email" name="email" required></label><br>
                <button type="submit">提交</button>
            </form>
            <a href="/">返回首页</a>
        </body>
        </html>
    )";
    auto resp = HttpResponse::newHttpResponse();
    resp->setBody(html);
    resp->setContentTypeCode(CT_TEXT_HTML);
    callback(resp);
}

// 3.2 处理表单提交(POST请求)
void handleSubmit(const HttpRequestPtr& req, function<void(const HttpResponsePtr&)>&& callback) {
    // 从表单中获取数据(POST表单数据在req->getParameters()中)
    auto params = req->getParameters();
    string username = params["username"];
    string email = params["email"];

    // 构建提交结果HTML
    string html = fmt::format(R"(
        <html>
        <head><title>提交成功</title></head>
        <body>
            <h1>表单提交成功!</h1>
            <p>您提交的信息:</p>
            <p>用户名: {}</p>
            <p>邮箱: {}</p>
            <a href="/form">返回表单</a> | <a href="/">返回首页</a>
        </body>
        </html>
    )", username, email);

    auto resp = HttpResponse::newHttpResponse();
    resp->setBody(html);
    resp->setContentTypeCode(CT_TEXT_HTML);
    callback(resp);
}


int main() {
    // 注册路由
    app().registerHandler("/", handleHome, {Get});               // 首页
    app().registerHandler("/form", showForm, {Get});             // 表单页面
    app().registerHandler("/submit", handleSubmit, {Post});      // 表单提交处理
    app().registerController(make_shared<UserController>());      // 注册用户控制器

    // 配置服务(端口8080,日志级别INFO)
    app().setLogLevel(trantor::Logger::kInfo);
    app().addListener("0.0.0.0", 8080);

    // 启动服务
    cout << "服务已启动,访问 http://localhost:8080" << endl;
    app().run();
    return 0;
}

编译代码

cpp 复制代码
g++ main.cpp -o drogon_app -ldrogon -std=c++17
  • -ldrogon:链接 Drogon 库
  • -std=c++17:指定 C++17 标准(Drogon 依赖)

运行程序

cpp 复制代码
./drogon_app

终端会输出 服务已启动,访问 http://localhost:8080,表示服务正常运行。

测试功能

  1. 访问 http://localhost:8080:查看首页,点击链接导航到其他页面。
  2. 访问 http://localhost:8080/user/1http://localhost:8080/user/2:查看模拟的用户信息。
  3. 访问 http://localhost:8080/form:填写表单并提交,会显示提交结果。

代码解析

  • 路由注册 :通过registerHandler直接注册简单路由(如首页、表单页),通过registerController注册控制器类(如用户信息页),支持 RESTful 风格的参数提取(/user/{id})。
  • HTTP 方法 :通过{Get}{Post}指定路由支持的 HTTP 方法,避免非法请求。
  • 响应处理 :通过HttpResponse构建响应,设置Content-Typetext/html以正确显示 HTML 内容。
  • 表单数据 :POST 表单数据通过req->getParameters()获取,键为表单字段名(如usernameemail)。

5.性能与适用场景

  • 性能:得益于异步 IO 和 C++ 的高效性,Drogon 在高并发场景下表现优异(可轻松处理数万 QPS)。
  • 适用场景:后端 API 服务、微服务、实时通信服务(如 WebSocket)、需要高性能的业务系统等。

6.总结

Drogon框架通过其异步非阻塞 的架构和丰富的功能集,为C++开发者提供了构建高性能Web服务的强大工具。无论是简单的API端点还是复杂的企业级应用,Drogon都能提供出色的性能和开发体验。

学习资源

相关推荐
m0_674031433 小时前
GitHub等平台形成的开源文化正在重塑林语堂
windows·mysql·spring
搬砖的小码农_Sky3 小时前
如何从Windows 操作系统登录Linux(Ubuntu)操作系统
linux·windows·ubuntu·远程工作
weixin_438077493 小时前
langchain_neo4j 以及 neo4j (windows-community) 的学习使用
windows·langchain·neo4j
搬砖的小码农_Sky4 小时前
如何在Linux(Ubuntu)操作系统上查看文件的MD5,SHA256等校验码
linux·运维·ubuntu
Dream it possible!4 小时前
LeetCode 面试经典 150_链表_随机链表的复制(59_138_C++_中等)
c++·leetcode·链表
我是华为OD~HR~栗栗呀4 小时前
华为od-22届考研-C++面经
java·前端·c++·python·华为od·华为·面试
码住懒羊羊4 小时前
【Linux】操作系统&进程概念
java·linux·redis
我是华为OD~HR~栗栗呀4 小时前
华为OD, 测试面经
java·c++·python·华为od·华为·面试
Wang's Blog5 小时前
Linux小课堂: 基于 SSH 的安全文件传输与增量同步机制深度解析之从 wget 到 rsync 的全流程实战
linux·ssh·1024程序员节