# httpserver-demo **Repository Path**: grassro0t/httpserver-demo ## Basic Information - **Project Name**: httpserver-demo - **Description**: 基于 **nginx + gRPC + Redis** 的 C++ 分层微服务 HTTP 后端 demo。 - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-22 - **Last Updated**: 2026-08-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # http_server_demo 基于 **nginx + gRPC + Redis** 的 C++ 分层微服务 HTTP 后端 demo。 - **nginx**:HTTP 入口,`upstream` 轮询负载均衡 - **gateway(HTTP 网关,可多实例)**:Drogon 框架,对外暴露 REST API - **grpc-server(业务服务)**:gRPC 实现 `UserService`,读写 Redis - **Redis**:用户数据存储 + 访问计数 ## 架构 ```mermaid flowchart LR C["客户端
(curl / 浏览器)"] -->|"HTTP :8080"| N["nginx
round-robin 负载均衡"] N -->|"反向代理"| G1["gateway-1 :8081
Drogon + gRPC client"] N -->|"反向代理"| G2["gateway-2 :8082
Drogon + gRPC client"] G1 -->|"gRPC :9090"| S["grpc-server :9090
UserService 实现"] G2 -->|"gRPC :9090"| S S -->|"读写 :6379"| R["Redis
JSON + 计数"] ``` 调用链:`curl :8080` → nginx 轮询选中一个 gateway → gateway 经 gRPC 调 grpc-server → grpc-server 读写 Redis → 逐层返回。 > 每个 HTTP 响应都带 `X-Gateway-Instance` 头,标识实际处理请求的 gateway 实例,用来肉眼观察负载均衡轮询效果。 ## 技术栈 | 层 | 选型 | |---|---| | 语言 | C++17 | | 构建 | CMake | | HTTP 网关 | Drogon 1.9 | | RPC | gRPC C++(brew 安装)+ protobuf | | 存储客户端 | hiredis(RAII 封装) | | JSON | nlohmann/json | | 负载均衡 | nginx | | 存储 | Redis | ## 目录结构 ``` http_server_demo/ ├── CMakeLists.txt # 构建配置 ├── Makefile # 常用命令封装 ├── proto/user/v1/user.proto # gRPC 契约 ├── generated/ # protoc 生成代码(已提交) ├── deploy/nginx/nginx.conf # 负载均衡配置 ├── src/ │ ├── common/ # 参数解析、JSON 工具 │ ├── store/ # hiredis RAII 封装 │ ├── grpc_server/ # UserService 实现 + 服务入口 │ └── gateway/ # Drogon 控制器 + gRPC client + 入口 └── build/ # 构建产物(gitignore) ``` ## 环境要求 macOS + Homebrew: ```bash brew install protobuf grpc pkg-config hiredis nlohmann-json redis nginx drogon ``` > 本仓库 `generated/` 已提交生成代码,因此**只要不修改 proto,编译阶段不需要手动跑 protoc**;若修改了 `user.proto`,用 `make proto` 重新生成。 ## 快速开始 ### 1. 编译 ```bash make build ``` ### 2. 启动服务(按顺序) ```bash # 终端 A:Redis redis-server # 终端 B:gRPC 业务服务 make run-grpc # 终端 C/D:两个 HTTP 网关实例 make run-gateway-1 make run-gateway-2 # 终端 E:nginx 负载均衡入口 make run-nginx ``` > 每个命令都应另开一个终端窗口(前台运行便于看日志)。也可以改为 `nohup ... &` 后台运行。 ### 3. 端到端演示 ```bash # 创建两个用户 curl -s -X POST http://127.0.0.1:8080/api/v1/users \ -H 'Content-Type: application/json' \ -d '{"name":"tom","email":"tom@example.com"}' curl -s -X POST http://127.0.0.1:8080/api/v1/users \ -H 'Content-Type: application/json' \ -d '{"name":"jerry","email":"jerry@example.com"}' # 查询单个用户 curl -s http://127.0.0.1:8080/api/v1/users/u1 # 用户列表(分页) curl -s 'http://127.0.0.1:8080/api/v1/users?limit=10&offset=0' # 统计信息(用户数 / 请求计数) curl -s http://127.0.0.1:8080/api/v1/stats # 观察负载均衡轮询:连续请求,观察 X-Gateway-Instance 头在 gateway-1/gateway-2 间切换 for i in $(seq 1 8); do curl -s -i http://127.0.0.1:8080/healthz | grep -i x-gateway-instance; done # 一行搞定全部 make test ``` > `make test` 需要所有服务已启动。 ### 4. 停止 ```bash make stop-nginx # 其余进程在各自终端 Ctrl+C 即可 ``` ## REST API | Method | Path | 说明 | |---|---|---| | POST | `/api/v1/users` | 创建用户,body `{"name": "...", "email": "..."}` | | GET | `/api/v1/users/{id}` | 按 ID 查询用户 | | GET | `/api/v1/users?limit=&offset=` | 分页列出用户 | | GET | `/api/v1/stats` | 统计:用户总数 + 请求计数 | | GET | `/healthz` | 健康检查,返回 `{"status":"ok","instance":"gateway-x"}` | 所有响应均为 JSON,并带 `X-Gateway-Instance` 头。 ## Redis 数据结构 | Key | 类型 | 说明 | |---|---|---| | `user:{id}` | String | 用户 JSON(主数据) | | `users:ids` | Set | 全部用户 ID,供列表遍历 | | `users:seq` | String(Int) | INCR 自增,用于生成用户 ID | | `stats:request_count` | String(Int) | INCR 原子计数,每次业务 RPC +1 | ## 常见问题 - **端口被占用**:确认 redis(6379)、grpc(9090)、gateway(8081/8082)、nginx(8080) 未被占用。 - **grpc-server 报 Redis 连接失败**:先启动 `redis-server`。 - **修改 proto 后代码没变**:执行 `make proto` 重新生成,再 `make build`。 - **nginx 启动失败**:查看 `deploy/nginx/logs/error.log`。 详细原理与踩坑记录见 **[NOTES.md](./NOTES.md)**。