# 负载均衡式实现LeetCode后端服务 **Repository Path**: Zhao_Y_Kun/OJ ## Basic Information - **Project Name**: 负载均衡式实现LeetCode后端服务 - **Description**: 分布式的OJ在线测评系统 - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: backup-2026-02-02_16-16-37 - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2025-02-22 - **Last Updated**: 2026-03-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README Distributed-OJ (负载均衡式高性能在线评测系统) ![Alt text]() 📌 项目简介 本项目是一个基于微服务架构思想实现的分布式在线评测系统(Online Judge)。系统通过 Muduo 高并发网络库 与 Redis 消息队列 实现了网关与判题后端的异步解耦,支持多判题节点自动负载均衡与高可用容灾。 在 1000 并发压测场景下,通过 perf 火焰图 定位并解决了底层内存锁竞争与数据库同步阻塞问题,将系统吞吐量从 72 QPS 提升至 560+ QPS(提升近 8 倍)。 🚀 核心架构演进:从 Push 到 Pull 项目经历了从 1.0 同步分发架构 到 2.0 异步解耦架构 的重大演进: v1.0 (负载均衡分发):网关通过 HTTP 同步调用后端判题机。缺点是网关需维护节点上下线状态,且易受后端慢处理影响导致线程池枯竭。 v2.0 (Redis 异步解耦):引入 Redis 消息队列作为中间件。 1. 生产者 (Gateway):仅负责任务合法性校验并 LPUSH 入队,实现极高并发响应。 2. 消费者 (JudgeServer):后端集群利用 BRPOP 阻塞拉取任务,天然实现基于节点算力的动态负载均衡,彻底屏蔽后端宕机对网关的影响。 🛠️ 技术栈与亮点 核心框架:C++11、Muduo (多 Reactor 模型)、cpp-httplib。 数据存储:MySQL (持久化题目与记录)、Redis (异步任务队列与判题缓存)。 通信协议:基于 JSON 的内部通信协议,保证了跨模块调用的灵活性。 性能优化: 1. 零拷贝技术:重构数据流转路径,使用常量指针与引用替代 std::string 深拷贝,消除高并发下的内存分配锁竞争。 2. 缓存预热:引入内存缓存机制,消除 MySQL 频繁建立 TLS 握手带来的同步阻塞。 📊 性能调优实战 (Performance Deep Dive) 本项目最核心的价值在于通过底层工具解决真实性能瓶颈: 1. 瓶颈定位 在初始压测中,系统在 1000 并发下出现大量 Timeout。使用 perf record 抓取采样数据并生成 火焰图 (Flame Graph)。 2. 问题分析 malloc 锁竞争:火焰图显示 _int_malloc 占据大量 CPU 周期,原因是高并发下 std::string 频繁申请/释放内存导致 glibc 底层锁等待。 MySQL 同步阻塞:libmysqlclient 在建立连接时的 TLS 握手造成了严重的线程挂起。 3. 优化结果 经过零拷贝重构与缓存优化,压测数据对比如下: | 指标 | 优化前 (v1.0) | 优化后 (v2.0) | 提升倍数 | | 吞吐量 (QPS) | 72 | 568 | ~8x | | 平均延迟 | > 2000ms | < 300ms | - | | 请求成功率 | < 10% | > 92% | - | 🛡️ 质量保障 1. 内存安全:通过 Valgrind 深度检测,确保核心业务逻辑 Definitely Lost 为 0,具备工业级稳定性。 2. 健壮性:集成故障检测机制。当判题节点意外宕机时,Redis 队列确保任务不丢失,新节点上线后可自动恢复消费。 📂 项目结构 ├── oj_server/ # API网关,负责任务下发与结果查询 ├── compile_server/ # 判题后端,基于线程池执行沙箱判题 ├── hiredis_adpater # redis交互工具(RedisUtile) ├── common/ # 公共工具类(Log, RedisUtil, MySQLUtil) ├── demo # 一些简单的测试demo └── scripts/ # 性能压测脚本 🛠️ 环境依赖与构建要求 本项目基于 Linux 环境开发(推荐 CentOS 7+),需使用支持 C++14 的编译器。 核心依赖库如下: 1. 网络与并发核心:Muduo 网络库(包含 muduo_net, muduo_base, muduo_http)、pthread。 2. 消息队列与缓存:Redis-server,及 C 语言客户端 hiredis。 3. 数据持久化:MySQL 数据库(需安装 mysql-devel),及 libmysqlclient。 4. 数据序列化:Jsoncpp(用于服务间通信协议封装)。 5. 前端渲染引擎:Google ctemplate(用于动态生成并渲染 HTML 页面)。 构建编译项目: 1. make all、make output 2. 打开output 3. 启动服务: 1. ./ojserver 2. ./compile_server (可启动多个) 其中最好compile_server放到资源较好的服务器上运行(本地测试使用的是8核8G的虚拟机) 并且内部需要修改下redis的ip使用本人的服务器ip、这里网关和redis在一台服务器上所以就不需要操作 更多配置(基本工具、防火墙等)请转到本人博客下查看:https://blog.csdn.net/ZYK069?spm=1010.2135.3001.5343 博客 分布式负载均衡OJ优化架构 压力测试: ![Alt text](docs/wrk.png) 减少用户到500 ![Alt text](docs/wrk2.png) 火焰图: ![Alt text](docs/fire.png) 内存泄漏检测: ![Alt text](docs/image.png) Valgrind 将内存状态分为四类,严重程度由高到低: 1. definitely lost (确切丢失) [关键指标:0 bytes]: 含义:你的程序 new/malloc 了内存,但在退出前完全丢失了指针,再也无法释放。 结论:这是最严重的 Bug,会导致程序随运行时间增加而内存耗尽。你的报告中这一项为 0,说明核心逻辑非常健康。 2. indirectly lost (间接丢失) [关键指标:0 bytes]: 含义:因为结构体或链表头被丢失,导致其内部指向的内存也找不到了。 结论:同样是必须修复的 Bug。你这里也是 0。 3. possibly lost (可能丢失) [关键指标:344 bytes]: 含义:指针依然存在,但不再指向内存块的起始位置(可能指向了块内部)。 结论:常见于复杂的内部指针操作或多线程干扰。344 bytes 极小,通常可以暂时忽略,除非数值在不断增长。 4. still reachable (仍可达) [关键指标:322,274 bytes]: 含义:程序退出时内存没释放,但指针还在。 分析:查看你的堆栈信息(record 757 & 758),内存是由 libmysqlclient 和 libcrypto 申请的。这是因为 MySQL 驱动在初始化(mysql_init)时会分配全局静态内存。 结论:这不是 Bug。这是第三方库的正常行为。只要这个数值不随请求量增加而爆炸式增长,就不需要处理。