Reverb WebSocket 配置与排障
Laravel Reverb 为项目提供实时 WebSocket 通信。本文档记录配置要点及常见问题的排查方法。
配置概览
环境变量 (.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=redisReverb 依赖 Redis 驱动的队列来派发广播事件,QUEUE_CONNECTION 必须为 redis。
Nginx 反向代理
Reverb 服务监在内网端口(如 8081),需要通过 Nginx 反向代理对外暴露 WebSocket:
# 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.1Upgrade/Connection头 — 触发 WebSocket 握手proxy_read_timeout 86400— 长连接保持 24 小时,避免被 Nginx 断开
Bootstrap 路由注册
bootstrap/app.php 需要注册 channels 路由文件,否则广播频道授权不生效:
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.phpinnopacks/panel/resources/views/layouts/app.blade.phpinnopacks/seller/resources/views/seller/layouts/app.blade.php
前端 JS 资源需要发布到 public/vendor/:
public/vendor/laravel-echo/echo.iife.jspublic/vendor/pusher-js/pusher.min.js
Supervisor 进程管理
Reverb 需要作为常驻进程运行,通过 Supervisor 管理(参考 进程管理):
[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
$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 Protocols 和 pusher:connection_established。
2. channels.php 未注册导致频道授权 404
现象: WebSocket 连接成功,但订阅私有频道时返回 404。
原因: bootstrap/app.php 中未注册 channels 路由文件,routes/channels.php 中的频道授权回调不会生效。
解决: 在 withRouting() 中添加 channels 参数:
->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.js 或 pusher.min.js 404。
解决: 发布前端资源:
php artisan vendor:publish --tag=laravel-echo
php artisan vendor:publish --tag=pusher-js或手动确认 public/vendor/laravel-echo/echo.iife.js 和 public/vendor/pusher-js/pusher.min.js 存在。
5. Echo 未初始化
现象: 页面加载后 window.Echo 未定义,控制台有 [Reverb] Echo initialization failed 警告。
排查:
- 检查
.env中BROADCAST_CONNECTION=reverb - 检查布局文件中是否包含
@include('common::components.echo') - 检查
config('broadcasting.connections.reverb')各项值是否正确 - 清除配置缓存:
php artisan config:clear
6. 队列未运行导致广播事件丢失
现象: 服务端触发了广播事件,但前端没有收到。
原因: 广播事件通过队列异步派发。如果 QUEUE_CONNECTION 不是 redis 或队列 Worker 未运行,事件会被丢弃。
解决:
- 确认
.env中QUEUE_CONNECTION=redis - 确认 Horizon / queue:work 进程正在运行
- 检查 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_PORT | ws://localhost:8080/app/...(直连) |
Echo 组件会检查 REVERB_FRONTEND_PORT,为空时不输出 wsPort/wssPort,Pusher JS 自动使用默认的 443 端口。
8. 端口冲突
现象: reverb:start 启动失败或报端口已被占用。
排查:
ss -tlnp | grep 8081如果端口被其他进程占用,修改 .env 中的 REVERB_PORT 并重启。
多项目部署时注意每个项目使用不同端口(如 Factory 用 8080,innoshop.cn 用 8081)。
快速验证清单
遇到 Reverb 问题时按以下顺序排查:
- 进程是否运行:
ps aux | grep reverb:start - 端口是否监听:
ss -tlnp | grep <port> - 直连是否正常: PHP WebSocket 握手脚本测试
127.0.0.1:<port> - Nginx 代理是否正常: PHP WebSocket 握手脚本测试
ssl://domain:443 - 环境变量是否正确:
php artisan config:show broadcasting - 前端资源是否存在:
ls public/vendor/laravel-echo/ public/vendor/pusher-js/ - 布局是否包含 Echo:
grep -r "components.echo" innopacks/*/resources/views/layouts/ - 队列是否运行:
php artisan horizon:status或检查 Supervisor