跳到主要内容
奇点CMS v0.4.70 · 官方网站 登录 / 注册 在线激活

聊天(IM)即时通信说明

两个关键的服务端约定:

шБКхдйхН│цЧ╢щАЪф┐бшп┤цШО.md 文档版本 v0.4.70 8 KB 更新于 2026-10-14

聊天(IM)即时通信说明

适用于 奇点CMS v0.3.96 及以后。本轮把聊天从"15 秒全量轮询"优化为"4 秒增量轮询 + 已读回执", 完整检测数据见交付记录中的《聊天插件即时通信检测报告》。

一、传输模型

项实现
传输方式HTTP 轮询(准实时;无 WebSocket / SSE)
会话窗口轮询4 秒(assets/im-plugin/modules/chat.js → ajax-chat-history.php,增量)
聊天主页轮询4 秒(messages.php 内联脚本,仅当 chat_with > 0 时启用)
未读徽标20 秒(assets/im-plugin/core/im-sdk.js → ajax-unread.php)
后台标签页暂停轮询(浏览器也会节流定时器);切回时立即补拉一次
隐藏期间新消息标题闪烁提示(不申请通知权限,避免打扰)

二、接口

接口说明
ajax-chat-history.php?chat_with=N全量:最近 30 条(按 id 升序返回),附 has_more、peer_read_id、incremental=false
ajax-chat-history.php?chat_with=N&since_id=M增量:只返回 id > M 的消息(最多 50 条),附 incremental=true;无新消息时响应约 128 字节
ajax-chat-send.php发送(需登录 + CSRF);支持文本与媒体(≤10 MB)
ajax-unread.php?csrf_token=…站内信/聊天未读数

两个关键的服务端约定:

  1. 仅在确有未读时才写库:只有"对方发给我且 is_read=0 的消息数 > 0"时才执行 UPDATE chat_messages SET is_read=1。 旧实现每次轮询都会 UPDATE 一次(无新消息也写),属于写放大。
  2. 消息顺序按主键 id:旧实现按 created_at(秒级 TIMESTAMP)排序,同一秒内的多条消息顺序不稳定。

三、已读回执

  • 数据来源:chat_messages.is_read(当对方打开你们的会话时由服务端置 1)。
  • 每次历史响应携带 peer_read_id = 我已发送、且对方已读的最大消息 id(0 表示对方尚未读)。
  • 前端在己方消息的时间行显示 已读 / 未读(.msg-read[data-read]),轮询时由 peer_read_id 实时校正。

四、实测数据(本机 Windows + 宝塔 + MySQL 8.0.29)

指标优化前优化后
接收延迟(会话窗口)0–15 s(均值 7.5 s)0–4 s(均值 2 s)
聊天主页 messages.php不刷新(需手动刷新页面)0–4 s 自动刷新
未读徽标刷新60 s20 s
空轮询响应体积15,328 B(重拉 30 条)128 B
无未读时的写库每次轮询 1 次 UPDATE0 次
已读回执无发送方可见"已读/未读"
单次轮询延迟20–47 ms10–47 ms
读查询计划(5 万行、目标会话 20 条)OR + ORDER BY created_atidx_conversation 范围扫描 + 20 行排序,0.41 ms
新增两个查询—走 idx_receiver_unread,0.03 ms
40 并发轮询2.2 s 完成(≈18 req/s)未变(容量非瓶颈)

结论:延迟问题解决,流量与写放大大幅下降,无需新增索引(已用 EXPLAIN ANALYZE 在 5 万行数据上验证)。 OR 改写成 UNION ALL 实测仅快约 2 倍(微秒级),收益不足以承担改动风险,故未采纳。

五、改前端资源时的注意点(重要)

assets/im-plugin/** 被 nginx 强缓存(JS/CSS 12h/30d)。**改动 im-plugin 下任何资源后,必须递增 assets/js/tab-card.js 里的 IM_BUILD 常量**(本版已从 20260929-5 递增为 20261002-1), 该构建号会随 im-sdk.js 的 URL 下发并传播给全部子资源,形成完整的缓存失效链;忘记递增会导致老用户继续执行旧代码。

六、C 档:Go SSE 推送网关(v0.3.97 已落地)

在 A+B 的 4 秒轮询之上增加实时推送通道,端到端延迟由秒级降到毫秒级;轮询保留为兜底,任何异常都不影响消息投递。

6.1 组件与数据流

浏览器 ──(1) GET ajax-im-token.php ──→ PHP:用 GO_API_KEY 签发短时令牌
        │      sig = HMAC-SHA256(key, "im:<user_id>|<ts>"),有效期 ±300s
        │
        └──(2) new EventSource("<站点>/im-stream?user_id&ts&sig")
                  │  nginx: location = /im-stream → proxy_buffering off → 127.0.0.1:8080/im/stream
                  ▼
              Go 服务:校验令牌 → 注册到 imHub(按 user_id 多路订阅)→ 20s 心跳保活
                  ▲
PHP ajax-chat-send.php ──(3) POST /api/im/publish(CSRF+HMAC 鉴权)──→ imHub 广播给接收者的所有连接

6.2 新增接口

接口鉴权说明
GET /im/stream?user_id&ts&sig短时令牌(PHP 签发)SSE 事件流;首帧 event: ready;event: chat 为聊天推送;: ping 每 20 s 保活
POST /api/im/publishX-API-Key + X-CSRF-Token + X-CSRF-Signature内部发布(PHP 调用),{"to":[uid...],"event":"chat","data":{...}} → 返回 delivered
GET /api/im/stats同上诊断:当前订阅连接数 / 用户数
GET ajax-im-token.php登录会话浏览器取令牌与同源订阅 URL;未配置 GO_API_KEY 时返回 enabled=false

6.3 安全设计

  • 浏览器不接触 GO_API_KEY,只拿短时令牌(300 s,且与 user_id 绑定,签名覆盖两者)。
  • 令牌校验失败/过期/缺失一律 403;GO_API_KEY 为空或默认值时直接禁用推送(避免可伪造)。
  • 事件负载只含提示性字段(发送者、预览片段、媒体类型、时间),不含消息正文全文;客户端收到事件后仍走鉴权接口增量拉取。
  • nginx 对该 location access_log off(令牌在查询串中);/api/im/* 沿用 Go 侧的 IP 白名单(仅回环)。

6.4 降级与容错

场景行为
未配置 GO_API_KEY / Go 未部署令牌接口返回 enabled=false,前端不再尝试,纯轮询照常
Go 服务重启 / 网络中断客户端指数退避重连(1→30 s),每次重连重新取令牌;期间轮询兜底
订阅者积压(慢客户端)Go 侧丢弃该事件(非阻塞),由轮询补齐
浏览器不支持 EventSource前端自动退出,纯轮询

6.5 实测(本机 Windows + 宝塔 + Go 1.22.12)

项结果
令牌校验错误签名 / 缺少签名 / 过期时间戳 → 403
订阅建立收到 event: ready;/api/im/stats → {connections:1, users:1}
发布{"success":true,"data":{"delivered":1,"targets":1}};无订阅者时 delivered:0
端到端延迟(测试实例直连)5 次连续发布→送达 14/16/17/24/29 ms(含测试端 10 ms 读取粒度)
端到端延迟(生产链路)浏览器 → nginx(/im-stream 反代,proxy_buffering off)→ Go:user1 真实 AJAX 发送(47 ms)→ user2 的 SSE 约 28 ms 收到事件
生产链路订阅/api/im/stats → {connections:1, users:1};首帧 event: ready
事件负载{"from":1,"from_name":"站长","preview":"…","media":"none","at":…} —— 只含预览片段,不含正文全文
UTF-8中文与 emoji 经 SSE 完整无损
推送失败容错Go 返回 404(旧版本 exe)时,ajax-chat-send.php 静默跳过,消息照常落库

6.6 部署要点

  1. Go 服务需重新编译(新增 im_hub.go / im_sse.go):go build -trimpath -ldflags "-s -w" -o x-dream-go.exe .
  2. nginx 需加 location = /im-stream 反代(nginx.conf.example 与 qdcms-deploy.ps1 生成的 vhost 已内置),必须 proxy_buffering off
  3. GO_API_KEY 已在 config/generated.php 中(Windows 由 qdcms-go-win.ps1 生成),PHP 与 Go 两侧一致即可
  4. 验证:浏览器登录后打开聊天窗口 → 用另一账号发消息,应在毫秒级出现(未启用时最多 4 秒)

原始文件:docs/шБКхдйхН│цЧ╢щАЪф┐бшп┤цШО.md · 出于安全考虑,站点不直接暴露 docs/ 目录下的原始文件(其中含内部资料),请在交付包内查看全文。

全部文档
站点公告 官网试运行中,如需购买或试用请通过「联系我们」留言。