文档版本 2026-05-29 · 目标:当前这台 VPS 要能扛住 10 万 MAU 而不倒下,应用则在时机成熟时无缝迁往 CDN。
| Metric | 取值 | Source |
|---|---|---|
| MAU | 100,000 | 目标 |
| DAU | 30,000 | 占 MAU 的 30%,工具类应用的常态 |
| 并发 WS | 10 000(峰值 1.5 万) | 白天约为 MAU 的 10%,峰值 15% |
| 每日操作数 | 90,000 | 每位日活用户 3 次开启或呼叫 |
| HTTP API 每秒请求数(平均) | 3-5 | 主要是 key_create、host_sync 和 crash_report |
| HTTP API 每秒请求数(峰值) | 30-50 | 在批量发布并更新号码之后 |
count=1 ——单个 PHP 进程把全部 WebSocket 连接放在数组里 $hosts, $guests, $calls in-memory.File: HostService.kt:1424-1432。目前 syncPendingHostKeys() 每 60 秒被调用一次,打向 Api.keyCreate ,对每个待处理密钥都不做任何退避。
存放于 Prefs per-localId: attempt_at_id 与 attempt_count_id。退避:30 秒 → 60 秒 → 2 分钟 → 5 分钟 → 15 分钟 → 30 分钟(封顶)。成功后重置。
File: Vault.kt:213。逻辑相同。
File: GuestConn.kt (5 处以上)。把新的 parseNumbers 与旧的比较,用哈希或直接比对。相同则不递增 AppState.overridesVersion。这样就免去了每次 WebSocket 初始化时的重组浪潮——80 次 SharedPreferences 读取 × 20 张卡片。
In server.php case 'host_hello' / 'guest_hello':如果同一个 device_id / user_key 在不到 N 秒前发过 hello,就直接关闭连接、不碰数据库。N = 5 秒。
用于防范逻辑损坏的客户端每秒发上百个 hello。
In enqueueHostMsg:如果该 host_id 已经超过 200 行,就用以下语句删掉最旧的一条 DELETE FROM pending_host_msgs WHERE host_id=? ORDER BY id ASC LIMIT 1。用于防止恶意活动导致队列无限增长。
In _config.php:
$GLOBALS['apk_url'] = 'https://entrixy.com/entrixy.apk';
$GLOBALS['download_page_url'] = 'https://entrixy.com/download';
In api/version_check.php: 'download_url' => $GLOBALS['apk_url'].
迁往 CDN 时,只需改动一行,位于 _config.php to https://apk.entrixy.com/entrixy.apk (Cloudflare R2 配自定义域名)。与应用发布无关。
客户端在下载时已经校验 APK 签名(调试密钥库,见 build.gradle.kts:23-29)。对 CDN 而言这已足够:即便 CDN 节点掉包,签名也对不上,Android 会拒绝安装。 客户端无需额外改动。
count=1 Workerman 轻松扛住约 1 万条并发 WebSocket——server.php 第 40 行就用它启动。第一个瓶颈会是消息交换高峰时 PHP 的 CPU 时间:每秒 1 万个 pong 再加上各类操作。
Today $hosts, $guests, $calls 是单个进程内的内存数组。要跑多个进程,就得把它们搬到 Redis:
消息落到工作进程 W1 而收件人在 W2 时,通过 Redis pub/sub 转发。
Effect: 按 VPS 核心数线性扩展。四核大约支撑 4 万并发。
灵活性差些,但不需要 Redis。每位用户总是落到同一个工作进程。可一旦发送方与接收方分处不同进程,这套就失效了。
entrixy.apk.apk.entrixy.com → 通过 Cloudflare CDN 接入。_config.php change $apk_url 改成新的。assembleRelease add r2 cp app-release.apk apk-bucket/entrixy.apk.