Moment · C++ 后端社区项目

基于 Drogon 框架,从零搭建用户系统、帖子 CRUD、评论点赞、分页、Redis 缓存、wrk 压测,直到集成本地 AI 聊天助手的完整学习路线。13 篇文档,覆盖从环境搭建到高并发优化的全部关键节点。

C++20 Drogon MySQL Redis llama.cpp SSE wrk CMake
13学习文档
3主题模块
7后端阶段
60+知识要点

WSL2 环境基础

先搭好开发环境,再开始写代码。覆盖从安装 WSL2 到配通完整 C++ 后端开发工具链的全部细节。

1
WSL2 搭建 Linux 开发环境
入门 ⏱ 约 30 分钟

零基础教程:在 Windows 10/11 上通过 WSL2 安装 Ubuntu 22.04,配置用户名、密码和基础开发工具链。不需要双系统或传统虚拟机,5 分钟理解 WSL2 的核心优势。

理解 WSL2 与 WSL1 的区别和适用场景
通过 Microsoft Store 或命令行安装 Ubuntu
wsl -l -v 验证安装并转换版本
初始化 Linux 用户、配置 C++ 编译工具链
掌握 wsl --shutdown 等常用管理命令
理解 /mnt/c/home 路径差异
核心命令
# 一键安装(管理员 PowerShell)
wsl --install
wsl --set-default-version 2

# 安装指定发行版
wsl --install -d Ubuntu-22.04

# 验证安装
wsl -l -v

# 安装开发工具链
sudo apt update && sudo apt install gcc g++ cmake make git -y
验收标准
  • wsl -l -v 显示 Ubuntu-22.04 且 VERSION 为 2
  • 能正常执行 sudo apt update 无报错
  • gcc --versioncmake --version 输出版本号
  • 家目录在 /home/用户名,不在 /mnt/c
2
VSCode 连接 WSL2 Linux 环境
入门 ⏱ 约 20 分钟

安装 WSL 扩展,用 3 种方式连接 Ubuntu 环境,为 WSL 安装 C/C++、CMake 等开发扩展,并完成第一个 Hello WSL2 程序的编译运行。覆盖 4 类常见问题的排查方案。

安装 WSL 扩展并连接 Linux 环境
区分 Windows 扩展和 WSL 扩展的安装位置
在 WSL 中编译运行 C++ 代码
排查连接失败、命令找不到、编译错误等问题
3 种连接方式
# 方式 1:命令面板(推荐)
Ctrl+Shift+P → WSL: Connect to WSL → 选择发行版

# 方式 2:从终端启动
cd ~/projects && code .

# 方式 3:远程资源管理器
安装 Remote Explorer 扩展 → 右键 Connect to WSL
验收标准
  • VSCode 左下角显示「WSL: Ubuntu-22.04」
  • 终端提示符为 Linux 风格 user@DESKTOP:~$
  • 编译运行 Hello WSL2 程序成功输出
3
WSL2 + VSCode + C++ 后端开发环境学习分享
进阶 ⏱ 约 60 分钟

科普型长文:先解释每个命令和软件包的含义,再一步步从 C++ 小程序走到 CMake 工程骨架,最后安装 Redis、MySQL、Protobuf 等后端依赖。适合作为知识库反复查阅。

掌握 20+ 条常用 Linux 命令及用途
理解 build-essential、pkg-config 等工具链
处理 apt 404 并切换到国内镜像源
建立 CMake 项目骨架(src/include/docs/scripts)
安装并验证 Redis、MySQL、Protobuf
理解为什么项目应放在 Linux 文件系统
关键步骤
# 修复 apt 404(换阿里云镜像)
sudo sed -i 's|http://archive.ubuntu.com/ubuntu|http://mirrors.aliyun.com/ubuntu|g' /etc/apt/sources.list
sudo apt clean && sudo apt update

# CMake 项目骨架
cmake_minimum_required(VERSION 3.16)
project(echo-server)
set(CMAKE_CXX_STANDARD 20)
add_executable(echo-server src/main.cpp)

# 安装后端依赖
sudo apt install -y protobuf-compiler libprotobuf-dev \
  mysql-server redis-server libmysqlclient-dev

# 验证
redis-cli ping  # → PONG
protoc --version
验收标准
  • 项目在 ~/workspace 下,不在 /mnt/c
  • CMake 能成功生成 echo-server 可执行文件
  • redis-cli ping 返回 PONG
  • 理解后续开发路线(先 HTTP → 再数据库 → 再业务 → 最后 IM)

Echo Server 核心路线

从最小 HTTP 服务到完整社区后端,按学习依赖顺序逐步推进。每一步都是上一步的自然延伸。

4
Echo Server:项目路线总览
入门 ⏱ 约 10 分钟

串联整个 Echo Server 系列的入口页。把 7 个阶段按学习依赖排序,说明每个阶段的定位和前后依赖关系,帮助你建立全局视角。

理解 7 个阶段的学习路线和依赖关系
知道每个阶段的核心交付物
确定自己的起点和下一步目标
5
Echo Server:Drogon 内存版用户系统
进阶 ⏱ 约 45 分钟

第一阶段:先把 HTTP 服务跑起来。安装编译依赖,用 Drogon 搭建最小服务,完成 /ping、注册、登录 3 个接口。用 unordered_map 临时保存用户,mutex 保护并发读写。

安装 JsonCpp、OpenSSL、UUID 等 Drogon 依赖
理解 Drogon HTTP 服务启动和路由注册
用 unordered_map + mutex 实现线程安全内存存储
用 curl 逐个验证 /ping、注册、登录接口
最小路由设计
GET  /ping                # 连通性测试
POST /api/auth/register  # 内存版注册
POST /api/auth/login     # 内存版登录

# 核心思路:
# 1. unordered_map<string, User> 保存用户
# 2. mutex 保护注册与登录的并发读写
# 3. makeJsonResponse 统一返回 JSON
验收标准
  • curl http://127.0.0.1:8080/ping 返回 200
  • 注册接口返回用户信息,重复注册返回错误
  • 登录接口正确验证账号密码并返回 token
6
Echo Server:MySQL 持久化用户系统
进阶 ⏱ 约 45 分钟

第二阶段:把内存临时账号升级为 MySQL 持久化。创建 echo_app 数据库和 echo_user 业务用户,设计 users 表(utf8mb4),让注册和登录接口直接操作 MySQL。

安装 MySQL 并创建数据库和业务用户
设计 users 表(含 password_hash、nickname、avatar_url)
用 Drogon DbClient 执行参数化 SQL
服务器重启后账号仍然存在
数据库设计
CREATE DATABASE IF NOT EXISTS echo_app
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CREATE USER 'echo_user'@'localhost' IDENTIFIED BY 'Echo123456';
GRANT ALL ON echo_app.* TO 'echo_user'@'localhost';

CREATE TABLE users (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50) NOT NULL UNIQUE,
  password_hash VARCHAR(255) NOT NULL,
  nickname VARCHAR(50),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_users_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
验收标准
  • 注册后 SELECT * FROM users 能看到新账号
  • 重启 MySQL 服务后登录仍然成功
  • 密码以 hash 形式存储,不是明文
7
Echo Server:帖子系统从 0 到 1
进阶 ⏱ 约 60 分钟

第三阶段:补齐社区项目核心 CRUD。设计 posts 表,实现 token 鉴权辅助函数,完成发帖、查最新、用户主页、帖子详情和删除自己的帖子 5 个接口。

设计 posts 表并建立 user_id 外键关联
实现 Authorization Bearer token 鉴权
完成 5 个帖子相关接口的 CRUD
按登录→发帖→查→删的顺序联调测试
接口设计
POST /api/posts/create     # 发帖(需 token)
GET  /api/posts/latest     # 最新帖子列表
GET  /api/posts/user       # 用户主页帖子
GET  /api/posts/detail     # 帖子详情
POST /api/posts/delete     # 删除自己的帖子(需 token)

# Token 鉴权核心:
std::string auth = req->getHeader("Authorization");
// 解析 "Bearer demo_token_用户ID"
验收标准
  • 无 token 发帖返回 401
  • 用户只能删除自己的帖子
  • 删除用户后其帖子被级联删除
8
Echo Server:评论、点赞与统计聚合
中高级 ⏱ 约 60 分钟

第四阶段:从「能发帖」升级到「能互动」。建立 comments 和 post_likes 两张表,实现评论、点赞、取消点赞,并用 LEFT JOIN + 子查询让列表直接返回统计数据。

设计 comments 表(含级联删除外键)
设计 post_likes 表(UNIQUE KEY 防重复点赞)
用 COALESCE + LEFT JOIN 聚合统计数据
一次查询返回 like_count 和 comment_count
聚合查询
SELECT p.id, u.username, p.content,
  COALESCE(lc.like_count, 0) AS like_count,
  COALESCE(cc.comment_count, 0) AS comment_count
FROM posts p
JOIN users u ON u.id = p.user_id
LEFT JOIN (
  SELECT post_id, COUNT(*) AS like_count
  FROM post_likes GROUP BY post_id
) lc ON lc.post_id = p.id
LEFT JOIN (
  SELECT post_id, COUNT(*) AS comment_count
  FROM comments GROUP BY post_id
) cc ON cc.post_id = p.id
ORDER BY p.id DESC LIMIT ? OFFSET ?;
验收标准
  • 同一用户重复点赞同一帖子不会报错(UNIQUE KEY)
  • 取消点赞后 like_count 正确减少
  • 帖子列表每条都包含 like_count 和 comment_count
9
Echo Server:分页与列表接口优化
进阶 ⏱ 约 30 分钟

第五阶段:把固定 LIMIT 改造为 page/page_size 参数。实现参数解析兜底函数,统一所有列表接口的分页行为,为后续压测提供稳定的测试对象。

实现 parsePositiveInt 参数校验函数
统一 page=1, page_size=20 的默认值
限制 page_size 最大值为 100
用 LIMIT ? OFFSET ? 占位符避免 SQL 注入
参数解析
int parsePositiveInt(const std::string& value,
                     int defaultVal, int maxVal) {
    try {
        int n = std::stoi(value);
        if (n <= 0) return defaultVal;
        if (n > maxVal) return maxVal;
        return n;
    } catch (...) { return defaultVal; }
}

int page = parsePositiveInt(req->getParameter("page"), 1, 100000);
int pageSize = parsePositiveInt(req->getParameter("page_size"), 20, 100);
int offset = (page - 1) * pageSize;
验收标准
  • page=-1 或 page_size=9999 不崩溃,回退默认值
  • 第 1 页和第 2 页数据不重叠
  • 所有列表接口分页行为一致

进阶优化与 AI 扩展

从普通 CRUD 升级为有高并发意识的后端项目,最后接入本地 AI 大模型,为项目增加差异化亮点。

10
Echo Server:压测、索引、Redis 与限流
中高级 ⏱ 约 90 分钟

第六阶段:不是盲目加 Redis,而是先压测、再分析 SQL、再消除 N+1、最后对热点接口做缓存和限流。用 wrk 用数据说话,体现高并发意识。

用 wrk 脚本压测 3 个核心接口
围绕 where/join/order by 建立索引
用 JOIN + 聚合消灭 N+1 查询
Redis 缓存用户资料和帖子详情
实现 IP 和用户维度的基础限流
压测与缓存策略
# wrk 压测(4线程 100并发 30秒)
wrk -t4 -c100 -d30s -L "http://127.0.0.1:8080/api/posts/latest?page=1&page_size=20"

# Redis 缓存 key 设计
user:profile:{user_id}       → 用户资料
post:detail:{post_id}        → 帖子详情
rate:ip:{ip}:{minute}        → IP 限流计数
rate:user:{user_id}:{minute} → 用户限流计数

# 读帖子详情流程:
# 1. GET Redis → 命中直接返回
# 2. 未命中 → 查 MySQL → SETEX 60s
验收标准
  • EXPLAIN 显示索引命中,type 不为 ALL
  • 加缓存后帖子详情 QPS 显著提升
  • 超出限流阈值返回 429 状态码
11
社区信息流高并发压测优化笔记
中高级 ⏱ 约 60 分钟

深入剖析 N+1 级联查库的致命成因,将循环同步查库重构为集合级 SQL 聚合操作。用户主页 4 合 1 多重聚合查询把 4 次网络往返压缩为 1 次。

理解 N+1 问题在高并发下的致命影响
用 LEFT JOIN 子查询消灭循环查库
实现 4 合 1 多重聚合查询优化主页
用 wrk 数据对比优化前后 QPS 变化
4 合 1 聚合查询
SELECT
  (SELECT COUNT(*) FROM posts WHERE user_id = ?) AS post_count,
  (SELECT COUNT(*) FROM posts WHERE user_id = ?
   AND image_url IS NOT NULL) AS media_count,
  (SELECT COUNT(*) FROM user_follows
   WHERE following_id = ?) AS follower_count,
  (SELECT COUNT(*) FROM user_follows
   WHERE follower_id = ?) AS following_count;
-- 4 次网络往返 → 1 次,QPS 直接翻倍
验收标准
  • 用户主页接口 P99 延迟降到 500ms 以下
  • 帖子列表每条数据的 like_count/comment_count 正确
  • SQL EXPLAIN 无 filesort 和全表扫描
12
Echo Server:本地 AI 聊天助手集成
高级 ⏱ 约 120 分钟

第七阶段:在本地 WSL 部署 llama.cpp 推理服务,下载 Qwen2.5-1.5B 量化模型,让 Drogon 后端通过 HTTP 调用本地大模型,并实现多轮会话和 SSE 流式输出。

源码编译 llama-server 和 llama-cli
下载并启动 Qwen2.5-1.5B 量化模型
设计 ai_conversations + ai_messages 会话表
实现 Drogon 异步流式 SSE 转发
前端用 ReadableStream 实现打字机效果
核心架构
# 1. 编译 llama.cpp
cd ~/workspace/echo-ai
cmake -B build
cmake --build build -j2 --target llama-server llama-cli

# 2. 启动本地推理服务(端口 18080)
./build/bin/llama-server \
  -m ./models/qwen2.5-1.5b/qwen2.5-1.5b-instruct-q4_k_m.gguf \
  --host 127.0.0.1 --port 18080 -c 2048

# 3. Drogon 连接本地 AI
g_aiClient = drogon::HttpClient::newHttpClient(
  "http://127.0.0.1:18080");
验收标准
  • llama-server 监听 18080 并正常响应
  • 多轮对话能记住上下文
  • 前端逐字显示 AI 回复(打字机效果)
  • 会话历史保存到 MySQL 并可回看
13
WSL2 + Drogon 深度接入本地 AI 推理助手
高级 ⏱ 约 90 分钟

深度补充文档:详细记录 llama.cpp 源码编译过程、Qwen 模型量化部署、MySQL 多轮对话表结构设计,以及 Drogon 异步流转发(AsyncStreamResponse)的实现细节。

理解 CPU 推理编译加速的原理
掌握 GGUF 量化模型的选择与下载
实现 Drogon AsyncStreamResponse 流式转发
用 Fetch ReadableStream 承接 SSE 数据流
Drogon SSE 转发核心
auto streamResp = HttpResponse::newAsyncStreamResponse(
  [](ResponseStreamPtr stream) {
    // 逐行转发 llama.cpp 的文本增量
    stream->send("data: {\"content\":\"片段\"}\n\n");
    // 收到 [DONE] 后断开
    stream->close();
  }
);
callback(streamResp);
验收标准
  • 前端收到 SSE 流并逐字渲染
  • 长文本生成过程不阻塞主线程
  • 会话和消息正确持久化到 MySQL
← 返回我的主页