# code_compiler **Repository Path**: yangtf/code_compiler ## Basic Information - **Project Name**: code_compiler - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-01-03 - **Last Updated**: 2026-01-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # C/C++/Java 编译器服务 一个基于 Go 的 HTTP 服务,提供 C、C++ 和 Java 代码编译和运行功能,支持可配置的并发、队列管理和超时处理。 ## 🌟 功能特性 ### 核心功能 - **多语言支持**: C (gcc)、C++ (g++ with C++17)、Java (OpenJDK 17) - **代码编译检查**: 快速验证代码语法正确性 - **代码在线执行**: 安全运行编译后的程序,支持输入输出 - **进程组隔离** (Linux): 使用Linux进程组机制,确保子进程被正确清理 - **资源限制**: - 时间限制:精确控制程序运行时间 - 内存限制:限制进程虚拟内存使用(Linux,使用setrlimit) - **超时控制**: 超时自动终止进程组,避免僵尸进程 - **并发控制**: Worker Pool模式限制并发任务数 - **队列管理**: 可配置最大队列大小,支持溢出处理 ### 安全特性 - **进程组隔离** (Linux): - 使用`setpgid`创建独立进程组 - 使用`kill(-pgid)`杀死整个进程组(包括所有子进程) - 确保fork的子进程被正确清理 - **资源限制**: - 内存限制通过`setrlimit(RLIMIT_AS)`实现 - 超时程序自动终止 - **临时目录隔离**: 每个任务在独立临时目录中编译运行 - **自动清理**: 任务完成后删除所有临时文件 ### 运维特性 - **健康检查**: `/api/health` 接口用于容器健康检查 - **统计信息**: `/api/stats` 接口返回队列和worker统计 - **优雅关闭**: 服务终止时正确清理资源 - **详细日志**: 结构化日志记录请求、任务生命周期和错误 - **CORS支持**: 允许跨域请求,方便Web前端调用 ## 📋 系统要求 ### 运行时要求 **Linux系统** (推荐): - Go 1.21 或更高版本 - gcc/g++ 编译器 - OpenJDK 17 (javac和java) - 代码运行功能完整支持(进程组隔离、内存限制) **Windows/macOS**: - Go 1.21 或更高版本 - gcc/g++ 编译器 - OpenJDK 17 - 代码运行功能基础支持(无进程组隔离) ### 开发环境 - Go 1.21+ - Git - Make 或构建脚本 ## 🚀 快速开始 ### 本地运行 ```bash # 1. 克隆仓库 cd code_compiler # 2. 安装依赖 go mod download # 3. 编译 go build -o compiler-service . # 4. 运行 ./compiler-service # 5. 测试 curl http://localhost:8080/api/health ``` ### Docker部署 ```bash # 1. 构建Linux版本 GOOS=linux GOARCH=amd64 go build -o compiler-service . # 2. 构建Docker镜像 docker build -t code-compiler . # 3. 运行容器 docker run -d -p 8080:8080 \ --name code-compiler \ -e MAX_CONCURRENCY=5 \ code-compiler ``` ## ⚙️ 配置说明 服务通过**命令行参数**或**环境变量**配置。优先级:**命令行参数 > 环境变量 > 默认值** ### 配置项 | 参数 | 环境变量 | 默认值 | 说明 | |------|---------|--------|------| | `-port` | `PORT` | 8080 | HTTP服务端口 | | `-max-concurrency` | `MAX_CONCURRENCY` | 5 | 最大并发编译任务数 | | `-max-queue-size` | `MAX_QUEUE_SIZE` | 100 | 任务队列最大长度 | | `-max-wait-time` | `MAX_WAIT_TIME` | 30s | 队列等待超时时间 | | `-temp-dir` | `TEMP_DIR` | /tmp/compiler | 临时文件目录 | | `-default-timeout` | `DEFAULT_TIMEOUT` | 10s | 默认执行超时时间 | ### 配置示例 ```bash # 使用默认值 ./compiler-service # 自定义配置 ./compiler-service \ -port=9090 \ -max-concurrency=10 \ -max-queue-size=200 # 使用环境变量 PORT=9090 MAX_CONCURRENCY=10 ./compiler-service # Docker环境变量 docker run -e PORT=9090 -e MAX_CONCURRENCY=10 code-compiler ``` ## 📡 API接口 ### POST /api/run - 编译和运行代码 **请求格式**: ```json { "code": "base64编码的源代码", // 必填 "input": "base64编码的输入数据", // 可选 "language": "c|cpp|java", // 可选,默认cpp "type": "compile|run", // 可选,默认compile "time_limit_sec": 10, // 可选,默认10秒 "memory_limit_mb": 512 // 可选,默认128MB } ``` **响应格式**: ```json { "success": true, "compilation": { "success": true, "error": "base64编码的错误信息(如果有)" }, "execution": { "success": true, "exit_code": 0, "stdout": "程序标准输出", "stderr": "程序错误输出", "execution_time": 0.123, // 执行时间(秒) "max_memory_usage_mb": 0.0, // 内存使用(MB) "error": "错误信息(如果有)" } } ``` ### GET /api/stats - 服务统计 ```json { "success": true, "data": { "active_workers": 3, "queue_length": 5, "max_queue_size": 100, "max_concurrency": 5 } } ``` ### GET /api/health - 健康检查 ```json { "status": "healthy", "timestamp": 1735659200 } ``` ## 💡 使用示例 ### 1. 仅编译检查 ```bash curl -X POST http://localhost:8080/api/run \ -H "Content-Type: application/json" \ -d '{ "code": "I2luY2x1ZGUgPHN0ZGlvLmg+CmludCBtYWluKCkgewogICAgcmV0dXJuIDA7Cn0=", "type": "compile", "language": "c" }' ``` ### 2. 编译并运行(简单程序) ```bash curl -X POST http://localhost:8080/api/run \ -H "Content-Type: application/json" \ -d '{ "code": "I2luY2x1ZGUgPHN0ZGlvLmg+CmludCBtYWluKCkgewogICAgcHJpbnRmKCJIZWxsbywgV29ybGQhXG4iKTsKICAgIHJldHVybiAwOwp9", "type": "run", "language": "c" }' ``` ### 3. 带输入的程序 ```bash curl -X POST http://localhost:8080/api/run \ -H "Content-Type: application/json" \ -d '{ "code": "I2luY2x1ZGUgPHN0ZGlvLmg+CmludCBtYWluKCkgewogICAgaW50IG51bTsKICAgIHByaW50ZigiRW50ZXIgYSBudW1iZXI6ICIpOwogICAgc2NhbmYoIiVkIiwgJm51bSk7CiAgICBwcmludGYoIllvdSBlbnRlcmVkOiAlZFxuIiwgbnVtKTsKICAgIHJldHVybiAwOwp9", "input": "NDI=", "type": "run", "language": "c" }' ``` ### 4. 超时控制 ```bash curl -X POST http://localhost:8080/api/run \ -H "Content-Type: application/json" \ -d '{ "code": "I2luY2x1ZGUgPHN0ZGlvLmg+CmludCBtYWluKCkgewogICAgd2hpbGUoMSkge30KICAgIHJldHVybiAwOwp9", "type": "run", "language": "c", "time_limit_sec": 3 }' ``` ### 5. Java程序 ```bash curl -X POST http://localhost:8080/api/run \ -H "Content-Type: application/json" \ -d '{ "code": "cHVibGljIGNsYXNzIE1haW4gewogICAgcHVibGljIHN0YXRpYyB2b2lkIG1haW4oU3RyaW5nW10gYXJncykgewogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigiSGVsbG8gZnJvbSBKYXZhISIpOwogICAgfQogICAgcHVibGljIHN0YXRpYyB2b2lkIG1haW4oU3RyaW5nW10gYXJncykgdGhyb3dzIEV4Y2VwdGlvbiB7CiAgICAgICAgU3lzdGVtLmV4aXQoMCk7CiAgICB9Cn0=", "type": "run", "language": "java" }' ``` ## 🔧 架构设计 ### 项目结构 ``` code_compiler/ ├── main.go # 应用入口 ├── internal/ # 内部包 │ ├── compiler/ # 编译器核心逻辑 │ │ └── compiler.go # 编译和运行协调 │ ├── runner/ # 代码执行包(新增) │ │ ├── runner.go # 执行器接口定义 │ │ ├── executor.go # 执行器核心逻辑 │ │ ├── processgroup_linux.go # Linux进程组隔离 │ │ └── processgroup_default.go# 非Linux回退实现 │ ├── config/ # 配置管理 │ │ └── config.go │ ├── queue/ # 任务队列 │ │ └── queue.go # Worker Pool实现 │ └── server/ # HTTP服务器 │ └── server.go # Gin路由和处理 ├── pkg/ # 公共包 │ └── models/ # 数据模型 │ └── models.go ├── Dockerfile # Docker镜像 ├── test_cases/ # 测试用例 │ ├── test_config.json # 测试配置 │ ├── hello_c.c # C示例 │ ├── hello_cpp.cpp # C++示例 │ ├── HelloJava.java # Java示例 │ ├── timeout_test.c # 超时测试 │ ├── fork_test.c # 进程组测试 │ └── memory_test.c # 内存限制测试 ├── run_tests.py # 测试脚本 ├── quick_test.py # 快速测试脚本 └── TESTING.md # 测试文档 ``` ### 执行流程 ``` HTTP请求 → Server.handleCompile() ↓ 创建CompilationTask ↓ 加入TaskQueue ↓ Worker获取任务 ↓ Compiler.ProcessTask() ├─ 编译阶段 │ ├─ 创建临时目录 │ ├─ 写入源代码 │ ├─ 调用编译器 (gcc/g++/javac) │ └─ 返回编译结果 │ └─ 运行阶段 (type="run") └─ runner.ExecuteProgram() ├─ 创建进程组 (Linux) ├─ 设置内存限制 (setrlimit) ├─ 启动进程 ├─ 超时控制 (select + time.After) ├─ 收集输出 └─ 清理进程组 (defer Kill) ``` ## 🧪 测试 ### 运行测试套件 ```bash # 安装Python依赖 pip install requests # 运行完整测试 python run_tests.py # 快速测试 python quick_test.py ``` ### 测试用例说明 | 测试用例 | 语言 | 类型 | 验证功能 | |---------|------|------|----------| | Basic C Hello World | c | run | 基础编译运行 | | C Program with Input | c | run | 输入处理 | | C Timeout Test | c | run | 超时控制 | | C Memory Limit Test | c | run | 内存限制 | | C Process Group Test | c | run | 进程组隔离 | | Basic C++ Hello World | cpp | run | C++编译运行 | | C++ Timeout Test | cpp | run | C++超时控制 | | Basic Java Hello World | java | run | Java编译运行 | | Java Program with Input | java | run | Java输入处理 | | Compilation Error Test | cpp | compile | 编译错误检测 | ## 🔒 安全特性详解 ### 进程组隔离 (Linux) **问题**: 用户代码可能fork子进程(如C的fork(),Python的subprocess) **解决方案**: ```go // 创建新进程组 cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: true, // 创建新进程组 } // 杀死整个进程组 syscall.Kill(-pgid, syscall.SIGKILL) // 负数PID表示进程组 ``` **效果**: - ✅ 主进程创建的所有子进程都在同一进程组 - ✅ 终止时杀死整个进程组,包括子进程和孙进程 - ✅ 无僵尸进程残留 ### 内存限制 (Linux) **实现**: 使用`setrlimit(RLIMIT_AS)`限制虚拟内存 ```go var rlimit syscall.Rlimit rlimit.Cur = uint64(memory_mb) * 1024 * 1024 rlimit.Max = rlimit.Cur syscall.Setrlimit(syscall.RLIMIT_AS, &rlimit) ``` **效果**: - ✅ 限制进程能使用的最大虚拟内存 - ✅ 超限时malloc/mmap失败,进程被杀死 - ✅ 防止恶意代码耗尽系统内存 ### 超时控制 **实现**: 使用Go的select + channel模式 ```go done := make(chan error, 1) go func() { done <- pg.Wait() }() select { case <-time.After(timeLimit): pg.Kill() // 超时杀死进程组 case err := <-done: // 正常结束 } ``` **效果**: - ✅ 精确控制执行时间 - ✅ 超时自动终止,释放资源 - ✅ defer确保进程总是被清理 ## 📊 性能优化 ### Worker Pool模式 - 限制并发数,避免资源耗尽 - 任务队列缓冲,处理突发请求 - Worker复用,减少goroutine创建开销 ### 资源限制 - 并发控制:避免同时运行太多编译任务 - 内存限制:防止单个任务占用过多内存 - 超时控制:防止任务无限期运行 ### 队列管理 - 队列满时拒绝新任务(503错误) - 等待超时防止阻塞 - 统计信息帮助监控负载 ## 🐳 Docker部署最佳实践 ### 推荐配置 ```yaml # docker-compose.yml version: '3' services: code-compiler: image: code-compiler:latest ports: - "8080:8080" environment: - MAX_CONCURRENCY=10 - MAX_QUEUE_SIZE=200 - DEFAULT_TIMEOUT=30s deploy: resources: limits: cpus: '2' memory: 2G reservations: memory: 512M restart_policy: condition: on-failure max_attempts: 3 ``` ### 资源限制 ```bash # 限制容器资源 docker run -d \ --name code-compiler \ --memory="2g" \ --cpus="2" \ -p 8080:8080 \ code-compiler ``` ## 📝 开发指南 ### 添加新语言支持 1. 在`internal/compiler/compiler.go`的`ProcessTask`中添加语言case 2. 实现`prepareCommand`支持新语言的执行方式 3. 在`test_cases/`中添加测试用例 4. 运行测试验证 ### 添加新的资源限制 1. 在`internal/runner/processgroup_linux.go`中实现 2. 使用对应的`setrlimit`常量 3. 更新文档说明 ## 🔍 故障排查 ### 问题:编译失败 **可能原因**: - 编译器未安装或不在PATH中 - 临时目录权限不足 - 源代码编码问题 **解决方法**: ```bash # 检查编译器 which gcc g++ javac java # 检查临时目录 ls -la /tmp/compiler # 查看日志 docker logs code-compiler ``` ### 问题:运行超时 **可能原因**: - 程序进入无限循环 - 程序等待输入 - time_limit_sec设置太小 **解决方法**: - 检查代码逻辑 - 提供input参数 - 增加time_limit_sec值 ### 问题:内存限制不生效 **可能原因**: - 非Linux平台(不支持setrlimit) - 权限不足 **解决方法**: - 确认在Linux环境运行 - 检查日志中的警告信息 - 使用Docker内存限制作为额外保护 ## 📚 相关文档 - [TESTING.md](TESTING.md) - 详细测试指南 - [API文档](docs/api.md) - 完整API参考 - [架构设计](docs/architecture.md) - 系统架构说明 ## 🤝 贡献指南 1. Fork本仓库 2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) 4. 推送到分支 (`git push origin feature/AmazingFeature`) 5. 开启Pull Request ## 📄 许可证 本项目采用 MIT 许可证 - 详见 LICENSE 文件 ## 🆘 支持和反馈 - 提交Issue:https://github.com/yourusername/code-compiler/issues - 讨论和交流:https://github.com/yourusername/code-compiler/discussions --- **最后更新**: 2025年1月 **维护者**: Development Team