mercure 不适合高频行情推送,因其基于 sse、吞吐低、无ack/重传/多路复用,仅适用于低频事件通知;高并发行情应采用原生 websocket 或独立服务。

Mercure 在 FrankenPHP 中不适合直接扛行情推送这种高频场景。它不是为毫秒级、每秒数千条 Tick 数据设计的,底层协议和默认配置都偏向“事件通知”而非“流式广播”。
Mercure 的设计定位和实际吞吐瓶颈
Mercure 协议本质是基于 Server-Sent Events(SSE)的轻量级服务端推送机制,适合用户登录通知、订单状态变更、后台任务完成这类低频、高语义、低数据量的事件。FrankenPHP 内置的 Mercure 实现复用了 Caddy 的事件分发模型,单连接默认最大并发订阅数约 100–200,消息堆积时会触发内存限流(mercure.publish_rate_limit 默认 100 req/s),且不支持消息批处理或二进制序列化。
行情推送要求的是:持续、低延迟、高吞吐、可丢弃(如非关键Tick)、支持多路复用。Mercure 的 HTTP/1.1 SSE 连接在面对每秒数百个客户端 × 每秒数十条更新时,容易出现:
- 连接数暴涨,触发系统
ulimit -n限制 - 服务端 JSON 序列化 + 字符串拼接成为 CPU 瓶颈
- 客户端重连频繁(SSE 自动重试机制在弱网下加剧抖动)
- 无原生心跳保活,长连接易被中间代理(如 CDN、NAT)静默断开
FrankenPHP 下更可行的行情推送路径
如果你已在用 FrankenPHP,又需要实现实时行情,建议绕过 Mercure,直接走更底层、更可控的方案:
- 用
frankenphp_handle_request()+ 原生 WebSocket 手写服务端逻辑(如public/tick.php),自行管理连接池、心跳、消息压缩(msgpack)、订阅路由 - 把行情源接入独立的轻量级 WebSocket 服务(如
ws://localhost:8080),FrankenPHP 只做反向代理和 TLS 终止(Caddy 配置里加reverse_proxy) - 对历史 K 线或日线类低频数据,才用 Mercure 推送“更新完成”信号,而不是推原始行情
注意:frankenphp_handle_request() 是阻塞式请求处理器,不能直接用于长连接;WebSocket 场景必须配合 ignore_user_abort(true) 和手动轮询 php://input 或使用 stream_socket_accept() 等底层 I/O 控制——这和 Laravel Octane 的 Swoole\WebSocket\Server 或 Workerman 的抽象层级不同,得自己兜底。
为什么有人误以为 Mercure 能扛住行情
因为 FrankenPHP 官方文档常把 Mercure 和 HTTP/3、Vulcain 并列宣传,容易让人忽略其适用边界。真实压测中,Mercure 在 500 客户端、每秒 20 条广播时就开始出现延迟毛刺;到 1000 客户端 + 每秒 50 条,平均延迟跳到 800ms 以上,错误率明显上升。这不是 FrankenPHP 的 bug,而是 SSE 协议本身的约束——它没有 ACK、没有重传、没有多路复用,只适合“尽力而为”的通知场景。
真正扛住行情推送的,是 AllTick 或 Finnhub 那类直连交易所、用 QUIC/WebSocket Binary Frame + 自定义帧头的专用通道。Mercure 在 FrankenPHP 里的价值,是帮你省掉一套独立的通知服务,而不是替代行情网关。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











