跳转至

实时通信示例

本文引用的文件
- websocket_ingress_runtime.v - inproc_vjsx_websocket_affinity_state.v - inproc_vjsx_websocket_affinity_runtime.v - websocket_echo_app.php - websocket_echo_app.js - app.mts - websocket-echo.toml - ws-min.toml - WEBSOCKET_MESSAGE_DISPATCH_PLAN.md - WEBSOCKET_PHASE2_IMPLEMENTATION_PLAN.md - WEBSOCKET_MVP_PLAN.md - README.md

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与扩展性
  8. 故障排查指南
  9. 结论
  10. 附录:配置与运行

简介

本指南面向希望在 vhttpd 上构建实时通信应用的开发者,围绕 WebSocket 连接的建立与管理、消息路由与广播、会话亲和性与负载均衡策略展开。通过两个最小可运行示例(PHP Echo 与 TypeScript 最小化 WS),演示双向实时通信的实现模式,并给出连接池管理、背压控制、错误重连与性能优化的最佳实践建议。

项目结构

仓库中与实时通信相关的代码与示例分布如下: - 协议入口与调度:vhttpd 内核负责 HTTP Upgrade 检测、WebSocket 握手、事件分发到 PHP 或 vjsx worker,以及命令执行与回写。 - 亲和性与负载均衡:vjsx executor 提供基于 key 的亲和性选择与 lane 绑定能力,支持从应用侧动态计算亲和键。 - 示例应用: - PHP Echo:使用 VSlim WebSocket App 在 PHP worker 中处理 open/message/close,返回 echo 响应。 - TS 最小化:通过 vjsx 暴露 websocket(frame) 处理器,返回命令列表完成 accept/send/join 等操作。 - 配置:分别提供 PHP Echo 与 TS 最小化的站点配置,启用 worker 池与静态资源托管等。

graph TB
Client["浏览器客户端"] --> Ingress["HTTP/WS 入口<br/>websocket_ingress_runtime.v"]
Ingress --> |Upgrade 成功| WSHub["本地 Hub/Presence<br/>注册连接与会话元数据"]
Ingress --> |dispatch 模式| Dispatch["消息派发器<br/>按事件调用 vjsx 处理器"]
Ingress --> |connection-hosted 模式| WorkerConn["Worker 长连接<br/>Unix Socket"]
Dispatch --> VJSX["vjsx 应用处理器<br/>app.mts"]
WorkerConn --> PHPApp["PHP WebSocket 应用<br/>websocket_echo_app.php"]
VJSX --> Commands["命令执行引擎<br/>send/join/broadcast/set_meta"]
WSHub --> Commands
Commands --> Client

图表来源 - websocket_ingress_runtime.v:279-411 - app.mts:5-43 - websocket_echo_app.php:224-239

章节来源 - websocket_ingress_runtime.v:1-482 - app.mts:1-44 - websocket_echo_app.php:1-240

核心组件

  • WebSocket 入口与升级
  • 校验 Upgrade 请求头,拒绝非 WS 请求;成功后接管连接并进入 WS 会话循环。
  • 支持两种模式:
    • connection-hosted:每个 WS 连接绑定一个 worker 长连接。
    • message-dispatch:vhttpd 持有连接,事件无状态派发到空闲 worker,worker 返回命令列表由 vhttpd 执行。
  • 事件与命令
  • 事件:open、message、close。
  • 命令:accept、send、close、join、leave、broadcast、set_meta 等。
  • 亲和性与负载均衡
  • 支持从查询参数、请求头或应用侧函数计算亲和键。
  • 可选择是否将连接钉住到特定 lane(线程/进程槽位),以维持局部状态一致性。
  • 示例应用
  • PHP Echo:接受连接后发送“已连接”,对消息进行前缀 echo,收到 bye 则关闭。
  • TS 最小化:open 时设置 meta、加入房间、同步初始状态;message 时根据 type 做 ping/pong 或回显。

章节来源 - websocket_ingress_runtime.v:20-30 - websocket_ingress_runtime.v:279-411 - inproc_vjsx_websocket_affinity_runtime.v:80-146 - websocket_echo_app.php:224-239 - app.mts:5-43

架构总览

下图展示了从浏览器到 vhttpd 再到 worker 的端到端流程,包括两种模式的差异与命令执行路径。

sequenceDiagram
participant C as "客户端"
participant I as "入口(Upgrade)"
participant H as "Hub/Presence"
participant D as "派发器(dispatch)"
participant W as "Worker(长连接)"
participant A as "应用处理器(PHP/vjsx)"
participant CMD as "命令执行"
C->>I : "GET /ws + Upgrade : websocket"
I->>I : "校验请求头"
alt "dispatch 模式"
I->>D : "构造 open 帧并派发"
D->>A : "调用 app.websocket(frame)"
A-->>D : "返回 {accepted, commands}"
D->>CMD : "执行命令(accept/send/join...)"
CMD-->>C : "握手成功/下发初始消息"
else "connection-hosted 模式"
I->>W : "建立 worker 长连接"
W->>A : "发送 open 事件"
A-->>W : "返回 accept/close/error/done"
W-->>I : "握手结果"
I-->>C : "101 Switching Protocols"
end
C->>I : "发送文本帧"
I->>H : "更新 presence/metadata"
I->>D : "message 事件派发(或转发至 W)"
D->>A : "处理业务逻辑"
A-->>D : "返回 send/close 等命令"
CMD-->>C : "推送响应/关闭连接"

图表来源 - websocket_ingress_runtime.v:279-411 - websocket_echo_app.php:224-239 - app.mts:5-43

详细组件分析

WebSocket 入口与升级流程

  • 入口职责
  • 识别 WS 升级请求,拒绝非法请求并返回 426。
  • 根据配置选择 dispatch 或 connection-hosted 模式。
  • 接管 TCP 连接,设置读写超时为无限,进入 WS 会话循环。
  • 关键回调
  • on_connect:注册连接到 Hub,记录 worker_socket、方法、路径、trace_id 等。
  • on_message:解析 opcode/payload,封装为 worker frame 写入 worker 通道,等待 done/close/error。
  • on_close:若非 worker 主动关闭,向 worker 发送 close 事件并清理资源。
flowchart TD
Start(["接收请求"]) --> Check["检查是否为 WS 升级"]
Check --> |否| Reject["返回 426 Upgrade Required"]
Check --> |是| Mode{"是否启用 dispatch?"}
Mode --> |是| DispatchOpen["派发 open 事件给 vjsx 处理器"]
DispatchOpen --> Accept{"accepted?"}
Accept --> |否| HTTPReject["返回 403/4xx 并结束"]
Accept --> |是| Takeover["接管连接并进入会话循环"]
Mode --> |否| LongLived["建立 worker 长连接并握手"]
LongLived --> Takeover
Takeover --> Loop["消息循环: 读帧 -> 派发 -> 写命令"]
Loop --> Close{"收到 close?"}
Close --> |是| Cleanup["注销连接并释放资源"]
Close --> |否| Loop

图表来源 - websocket_ingress_runtime.v:20-30 - websocket_ingress_runtime.v:279-411 - websocket_ingress_runtime.v:413-481

章节来源 - websocket_ingress_runtime.v:1-482

消息路由与广播机制

  • 路由
  • 同一连接的事件在 vhttpd 层序列化,避免乱序。
  • 支持 join/leave 房间,presence 维护成员集合与计数。
  • 广播
  • 命令包含 broadcast,用于向房间内所有成员推送消息。
  • 命令执行引擎会遍历房间成员并写出对应帧。
  • 元数据与会话亲和
  • set_meta 可在连接级别附加元数据,便于后续路由与审计。
  • 结合亲和键,可将相关连接定向到相同 lane,提升缓存命中率与一致性。

章节来源 - websocket_ingress_runtime.v:279-411 - WEBSOCKET_MESSAGE_DISPATCH_PLAN.md:1-412

会话亲和性与负载均衡策略

  • 亲和键来源
  • 可从查询参数、请求头或应用侧函数计算得到。
  • 支持 fallback 策略:reject 或 round_robin。
  • Lane 绑定
  • should_pin_lane 决定是否将连接钉住到特定 lane。
  • 迁移与释放:当连接亲和键变化或连接关闭时,更新引用计数并清理映射。
  • 决策流程
  • 若已有连接级亲和键则复用;否则按 source 计算新键。
  • 若未命中且 fallback=reject,直接拒绝请求。
classDiagram
class ExecutorState {
+map conn_to_key
+map conn_to_lane
+map key_to_ref_count
+map key_to_lane
+list pending_keys
+release(key)
+migrate(frame,key,lane)
}
class AffinityRuntime {
+acquire_websocket_lane(frame) (lane,key)
+resolve_websocket_dispatch_affinity(frame) (key,priority,pin)
-request_lane_affinity(app,lane,frame)
}
ExecutorState <.. AffinityRuntime : "读取/更新亲和状态"

图表来源 - inproc_vjsx_websocket_affinity_state.v:6-124 - inproc_vjsx_websocket_affinity_runtime.v:80-186

章节来源 - inproc_vjsx_websocket_affinity_state.v:1-124 - inproc_vjsx_websocket_affinity_runtime.v:1-186

示例一:PHP WebSocket Echo

  • 功能要点
  • HTTP 页面返回 HTML,内嵌 JS 客户端。
  • WS 端点 /ws:onOpen 接受连接并发送“已连接”;onMessage 对文本进行 echo;收到 bye 则关闭。
  • 前端交互
  • 提供连接、发送、断开、清空日志等按钮,统一输出时间戳日志。
  • 适用场景
  • 快速验证 WS 链路、调试网络问题、演示基本收发语义。

章节来源 - websocket_echo_app.php:182-239 - websocket_echo_app.js:1-92

示例二:TypeScript 最小化 WS

  • 功能要点
  • 通过 vjsx 暴露 websocket(frame) 处理器。
  • open 时设置 meta、加入房间、同步初始状态;message 时根据 type 做 ping/pong 或回显。
  • 适用场景
  • 展示 vjsx 模式下命令式编程模型,适合复杂路由与房间协作。

章节来源 - app.mts:1-44

依赖关系分析

  • 入口模块依赖
  • 导入 net.websocket、net.http、worker、ws 等模块,实现握手、帧编解码与事件桥接。
  • 与 vjsx executor 的耦合
  • 通过 affinity runtime 获取 lane 与亲和键,影响后续任务调度与队列行为。
  • 示例与应用
  • PHP 示例依赖 VSlim WebSocket App;TS 示例依赖 vjsx 运行时与命令执行引擎。
graph LR
Ingress["websocket_ingress_runtime.v"] --> WS["ws 模块"]
Ingress --> Worker["worker 模块"]
Ingress --> NetWS["net.websocket"]
Ingress --> Exec["executor 亲和性模块"]
Exec --> State["亲和状态(state)"]
PHP["websocket_echo_app.php"] --> Ingress
TS["app.mts(vjsx)"] --> Ingress

图表来源 - websocket_ingress_runtime.v:1-20 - inproc_vjsx_websocket_affinity_runtime.v:1-44 - inproc_vjsx_websocket_affinity_state.v:1-42 - websocket_echo_app.php:224-239 - app.mts:5-43

章节来源 - websocket_ingress_runtime.v:1-482 - inproc_vjsx_websocket_affinity_runtime.v:1-186 - inproc_vjsx_websocket_affinity_state.v:1-124

性能与扩展性

  • 连接与 Worker 解耦
  • 采用 message-dispatch 模式后,连接数不再与 worker 数量强耦合,worker 仅在有事件时参与处理,显著提升吞吐。
  • 背压控制
  • 建议在 vhttpd 层为每连接维护事件队列,限制并发 in-flight 任务,防止 worker 过载。
  • 对命令执行增加超时与重试上限,避免长时间占用资源。
  • 亲和性与缓存
  • 合理设置亲和键与 pin 策略,减少跨 lane 状态迁移成本,提高内存缓存命中率。
  • 资源回收
  • 确保 close 事件能正确触发 worker 侧清理,避免泄漏;对异常路径补充日志与指标。
  • 参考规划
  • MVP 范围聚焦于 text 帧、open/message/close 与基础命令集,逐步演进到更复杂的房间与集群广播。

章节来源 - WEBSOCKET_MESSAGE_DISPATCH_PLAN.md:288-339 - WEBSOCKET_PHASE2_IMPLEMENTATION_PLAN.md:378-431 - WEBSOCKET_MVP_PLAN.md:65-121

故障排查指南

  • 常见错误码与原因
  • 426 Upgrade Required:请求不是 WS 升级请求或缺少必要头部。
  • 403 Forbidden:应用拒绝接受连接或未返回 accept。
  • 502 Bad Gateway:worker 不可用或握手失败。
  • 500 Worker Runtime Error:应用侧抛出错误。
  • 定位步骤
  • 检查入口日志中的 trace_id 与 path,确认是否正确进入 WS 分支。
  • 核对 worker 侧返回的命令序列,确认是否存在 close/error/done。
  • 观察 presence 与房间成员变化,确认 join/broadcast 是否生效。
  • 恢复策略
  • 客户端实现指数退避重连,区分网络错误与服务端拒绝。
  • 服务端对频繁失败的连接实施限流或熔断。

章节来源 - websocket_ingress_runtime.v:279-411

结论

vhttpd 提供了统一的传输运行时,既能承载传统 HTTP,也能高效处理 WebSocket 与流式协议。通过消息派发模式与亲和性策略,可以在保持低延迟的同时获得良好的可扩展性。配合示例应用与配置,开发者可以快速搭建可靠的实时通信服务,并在生产环境中持续优化性能与稳定性。

附录:配置与运行

  • PHP Echo 示例
  • 站点监听端口、worker 自动启动、事件日志与静态资源根目录均可在配置中指定。
  • TS 最小化示例
  • 启用 websocket_dispatch,指定 vjsx 应用路径与运行时 profile,设置线程数。
  • 通用工作进程参数
  • 可通过命令行参数调整 worker 池大小、重启策略、admin 平面等。

章节来源 - websocket-echo.toml:1-28 - ws-min.toml:1-16 - README.md:1065-1089