Skip to content

Reverb WebSocket 配置与排障

Laravel Reverb 为项目提供实时 WebSocket 通信。本文档记录配置要点及常见问题的排查方法。

配置概览

环境变量 (.env)

env
BROADCAST_CONNECTION=reverb

REVERB_APP_ID=your_app_id
REVERB_APP_KEY=your_app_key
REVERB_APP_SECRET=your_app_secret
REVERB_HOST=www.innoshop.cn
REVERB_PORT=8081
REVERB_SCHEME=https

# 生产环境(有 nginx 反代):设为空,前端走 443 端口由 nginx 代理
# 本地开发(无反代):不设此变量,自动使用 REVERB_PORT 直连
REVERB_FRONTEND_PORT=

QUEUE_CONNECTION=redis

Reverb 依赖 Redis 驱动的队列来派发广播事件,QUEUE_CONNECTION 必须为 redis

Nginx 反向代理

Reverb 服务监在内网端口(如 8081),需要通过 Nginx 反向代理对外暴露 WebSocket:

nginx
# WebSocket proxy for Reverb
location /app {
    proxy_pass http://127.0.0.1:8081;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;
}

关键点:

  • proxy_http_version 1.1 — WebSocket 升级必须使用 HTTP/1.1
  • Upgrade / Connection 头 — 触发 WebSocket 握手
  • proxy_read_timeout 86400 — 长连接保持 24 小时,避免被 Nginx 断开

Bootstrap 路由注册

bootstrap/app.php 需要注册 channels 路由文件,否则广播频道授权不生效:

php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        channels: __DIR__.'/../routes/channels.php',  // 必须添加
        health: '/up',
    )

前端 Echo 组件

Echo 组件位于 innopacks/common/resources/views/components/echo.blade.php,在各布局中通过 @include('common::components.echo') 引入。

三个布局均已包含:

  • innopacks/front/resources/views/layouts/app.blade.php
  • innopacks/panel/resources/views/layouts/app.blade.php
  • innopacks/seller/resources/views/seller/layouts/app.blade.php

前端 JS 资源需要发布到 public/vendor/

  • public/vendor/laravel-echo/echo.iife.js
  • public/vendor/pusher-js/pusher.min.js

Supervisor 进程管理

Reverb 需要作为常驻进程运行,通过 Supervisor 管理(参考 进程管理):

ini
[program:innocn-reverb]
command=php /www/wwwroot/www.innoshop.cn/artisan reverb:start --port=8081
autostart=true
autorestart=true

常见问题

1. curl 测试返回 500 / Internal Server Error

现象: 用 curl 直接访问 https://example.com/app/your-key 返回 500。

原因: Reverb 只接受 WebSocket 升级请求。普通 HTTP 请求(没有 Upgrade: websocket 头)会被拒绝并返回 500。这是 正常行为,不代表 Reverb 有故障。

验证方法: 使用 PHP 脚本模拟 WebSocket 握手:

php
<?php
$sock = stream_socket_client("tcp://127.0.0.1:8081", $errno, $errstr, 5);
$key = base64_encode(random_bytes(16));
$headers = "GET /app/your_app_key HTTP/1.1\r\n"
    . "Host: 127.0.0.1:8081\r\n"
    . "Upgrade: websocket\r\n"
    . "Connection: Upgrade\r\n"
    . "Sec-WebSocket-Key: $key\r\n"
    . "Sec-WebSocket-Version: 13\r\n\r\n";
fwrite($sock, $headers);
stream_set_timeout($sock, 3);
echo fread($sock, 4096);
fclose($sock);

期望看到 101 Switching Protocolspusher:connection_established

2. channels.php 未注册导致频道授权 404

现象: WebSocket 连接成功,但订阅私有频道时返回 404。

原因: bootstrap/app.php 中未注册 channels 路由文件,routes/channels.php 中的频道授权回调不会生效。

解决:withRouting() 中添加 channels 参数:

php
->withRouting(
    // ...
    channels: __DIR__.'/../routes/channels.php',
)

3. HTTP/2 导致 WebSocket 升级失败

现象: 直接连接 Reverb 端口正常,通过 Nginx HTTPS 代理时 WebSocket 握手失败。

原因: Nginx 配置了 http2 on;,但 WebSocket 升级需要 HTTP/1.1。浏览器会自动处理协议协商(先以 HTTP/1.1 发起 WebSocket 请求),但某些客户端或测试工具可能出问题。

说明: 实际上主流浏览器在发起 WebSocket 连接时会自动降级到 HTTP/1.1,所以 http2 on; 通常不会影响前端正常使用。如果确实遇到问题,可以在 /app location 中强制使用 HTTP/1.1(Nginx 代理配置中已有 proxy_http_version 1.1)。

4. 前端 JS 资源缺失

现象: 浏览器控制台报 echo.iife.jspusher.min.js 404。

解决: 发布前端资源:

bash
php artisan vendor:publish --tag=laravel-echo
php artisan vendor:publish --tag=pusher-js

或手动确认 public/vendor/laravel-echo/echo.iife.jspublic/vendor/pusher-js/pusher.min.js 存在。

5. Echo 未初始化

现象: 页面加载后 window.Echo 未定义,控制台有 [Reverb] Echo initialization failed 警告。

排查:

  1. 检查 .envBROADCAST_CONNECTION=reverb
  2. 检查布局文件中是否包含 @include('common::components.echo')
  3. 检查 config('broadcasting.connections.reverb') 各项值是否正确
  4. 清除配置缓存:php artisan config:clear

6. 队列未运行导致广播事件丢失

现象: 服务端触发了广播事件,但前端没有收到。

原因: 广播事件通过队列异步派发。如果 QUEUE_CONNECTION 不是 redis 或队列 Worker 未运行,事件会被丢弃。

解决:

  1. 确认 .envQUEUE_CONNECTION=redis
  2. 确认 Horizon / queue:work 进程正在运行
  3. 检查 Horizon Dashboard(/horizon)查看失败任务

7. 前端直连 Reverb 端口(绕过 nginx 反代)

现象: 浏览器连接 wss://www.innoshop.cn:8081/app/...,而不是 wss://www.innoshop.cn/app/...。部分网络环境可能封锁非标端口导致连接失败。

原因: Echo 组件直接使用了 REVERB_PORT(8081)作为前端连接端口。生产环境有 nginx 反代时,前端应该走 443 端口,由 nginx 转发到内部 Reverb。

解决: 通过 REVERB_FRONTEND_PORT 环境变量控制:

场景.env 配置前端连接地址
生产(有 nginx 反代)REVERB_FRONTEND_PORT=(空值)wss://www.innoshop.cn/app/...(走 443)
本地开发(无反代)不设此变量,自动用 REVERB_PORTws://localhost:8080/app/...(直连)

Echo 组件会检查 REVERB_FRONTEND_PORT,为空时不输出 wsPort/wssPort,Pusher JS 自动使用默认的 443 端口。

8. 端口冲突

现象: reverb:start 启动失败或报端口已被占用。

排查:

bash
ss -tlnp | grep 8081

如果端口被其他进程占用,修改 .env 中的 REVERB_PORT 并重启。

多项目部署时注意每个项目使用不同端口(如 Factory 用 8080,innoshop.cn 用 8081)。

快速验证清单

遇到 Reverb 问题时按以下顺序排查:

  1. 进程是否运行: ps aux | grep reverb:start
  2. 端口是否监听: ss -tlnp | grep <port>
  3. 直连是否正常: PHP WebSocket 握手脚本测试 127.0.0.1:<port>
  4. Nginx 代理是否正常: PHP WebSocket 握手脚本测试 ssl://domain:443
  5. 环境变量是否正确: php artisan config:show broadcasting
  6. 前端资源是否存在: ls public/vendor/laravel-echo/ public/vendor/pusher-js/
  7. 布局是否包含 Echo: grep -r "components.echo" innopacks/*/resources/views/layouts/
  8. 队列是否运行: php artisan horizon:status 或检查 Supervisor

帆连科技 · 基于 OSL 3.0 许可发布