# SpeedTest · 网速测试服务 基于 **Go + Gin + SQLite (GORM)** 的网页网速测试服务,支持 **延迟、下载、上传** 三项测试, 测速结果自动入库并生成 **排行榜**(下载榜 / 上传榜 / 延迟榜)。 - 线上地址: https://speedtest.lmve.net - 代码仓库: https://git.lmve.net/dsh/speedtest - 技术栈: Go 1.25 / Gin / GORM / SQLite (CGO) / TOML 配置 / systemd / Caddy ## 功能特性 - ⚡ **延迟测试**: 前端连续 10 次 ping,取中位数作为延迟、平均绝对偏差作为抖动 - ⬇️ **下载测试**: 固定测速 10 秒——负载根据实测速率自适应(每轮约 2 秒), 快速连接自动加大负载填满 10 秒,慢速连接用小负载同样 10 秒内完成 - ⬆️ **上传测试**: 同样的固定 10 秒自适应策略 - 🏆 **排行榜**: 下载 / 上传 / 延迟 三个榜单各取 Top 10,公网 IP 打码(保留前 3 段); 内网来源 IP(NAT 场景)完整标注,方便站长调试区分同一出口下的不同设备 - 🌐 **公网 IP 识别**: 服务器直接识别客户端真实 IP(Caddy `X-Real-IP`), 不依赖任何外部 IP 查询服务;路由器单向伪装/公网直连时即为真实公网 IP - 🔒 **Unix socket 监听**: 由 Caddy 反代对外提供 HTTPS,不暴露 TCP 端口 ## 项目结构 ``` speedtest/ ├── main.go # 入口:加载配置 → 初始化数据库 → 启动 Web ├── install.sh # 一键安装/更新/卸载/管理脚本(systemd) ├── config/ │ ├── config.go # TOML 配置加载(缺失自动生成/补全) │ └── defaults.go # 默认路径与默认值 ├── internal/ │ ├── db/ │ │ ├── db.go # GORM 初始化 + 自动迁移 │ │ └── models.go # SpeedTestResult 数据模型 │ ├── store/ │ │ ├── stores.go # 数据访问层聚合 │ │ └── result_store.go # 测速结果增查(Top N / Count) │ └── web/ │ ├── server.go # Gin 引擎 + 路由 + Unix socket/TCP 监听 │ ├── handlers/ │ │ └── speedtest.go # ping/download/upload/result/rankings │ └── templates/ │ └── index.html # 前端单页(原生 JS,无外部依赖) ``` ## 本地开发 ```bash # 编译(SQLite 依赖 CGO) CGO_ENABLED=1 go build -ldflags="-s -w" -o speedtest . # 本地运行(可用 SPEEDTEST_CONFIG 覆盖配置路径,见 test.toml 示例) SPEEDTEST_CONFIG=./test.toml ./speedtest ``` ## 部署(生产) ```bash sudo ./install.sh install ``` 脚本会自动完成:创建 `speedtest` 系统用户 → 创建目录 → 从 Gitea 拉取代码 → 编译(CGO_ENABLED=1)→ 部署二进制与模板 → 生成 systemd 服务 → 启动并设置开机自启。 | 项目 | 路径 | | --- | --- | | 程序目录 | `/opt/speedtest` | | Unix socket | `/opt/speedtest/web.sock` | | 配置文件 | `/etc/speedtest/speedtest.toml` | | SQLite 数据库 | `/srv/speedtest/speedtest.db` | | 日志 | `journalctl -u speedtest -f` | ### Caddy 反代配置 ```caddy speedtest.lmve.net { reverse_proxy unix//opt/speedtest/web.sock { header_up X-Real-IP {remote_host} header_up Host {host} } } ``` ## 配置文件 程序首次启动自动生成 `/etc/speedtest/speedtest.toml`(缺失项自动补全): ```toml [database] driver = "sqlite" dsn = "/srv/speedtest/speedtest.db" [web] addr = "/opt/speedtest/web.sock" # 以 / 开头为 unix socket,否则为 TCP 端口 [speedtest] max_download_bytes = 536870912 # 单次下载请求上限(默认 512 MB) max_upload_bytes = 536870912 # 单次上传请求上限(默认 512 MB) ``` > 注:默认上限 512MB 服务于前端自适应测速(快速连接负载会翻倍增长); > 旧版本配置中的 100MB 上限可手动调大,或删除配置行后重启让程序补全默认值。 ## API | 方法 | 路径 | 说明 | | --- | --- | --- | | GET | `/` | 测速页面 | | GET | `/api/ping` | 延迟探测(返回 `{"pong":true,"ts":...,"client_ip":...}`) | | GET | `/api/download?size=N` | 下载测速,流式返回 N 字节随机数据(默认 10MB,上限 512MB) | | POST | `/api/upload` | 上传测速,接收请求体并返回 `received/elapsed_s/mbps` | | POST | `/api/result` | 提交结果 `{latency_ms, jitter_ms, download_mbps, upload_mbps, client_ip?, server_ip?}` | | GET | `/api/rankings` | 排行榜(`download/upload/latency` 三榜 + `total`,含 `is_private_ip` 与来源 `server_ip`) | 客户端 IP 由服务器直接识别(Caddy 注入的 `X-Real-IP`),前端不调用任何外部 IP 服务; `client_ip` / `server_ip` 字段为 API 兼容保留(可选)。公网 IP 打码保护隐私; NAT 场景下的内网来源 IP 在排行榜中完整标注,供站长调试区分设备。