symfony 7.0 无法原生支持 websocket,必须通过独立进程(如 swoole 或 ratchet)承载 websocket 服务,并借助 redis 发布订阅机制与 symfony 业务逻辑解耦通信,前端通过 nginx 反向代理建立 wss 连接实现实时推送。

要在 Symfony 7.0 中实现 WebSocket 实时通信,必须明确一点:Symfony 本身不内置 WebSocket 服务器能力,它是一个 HTTP 应用框架,无法原生维持长连接。直接在 Symfony 的 HTTP 内核中启动 WebSocket 服务会导致连接被 FPM 或 Apache/Nginx 中途切断,【连接必然断开且无法恢复】。因此,正确路径是将 WebSocket 服务与 Symfony 解耦,由独立进程承载,再通过 Redis、消息队列或 HTTP API 与 Symfony 业务逻辑协同。
第一步:选择并部署独立 WebSocket 服务
推荐使用 Swoole(PHP 8.2+)或 Ratchet(兼容性更广),二者均支持 Symfony 7.0 环境。
方法一(Swoole,高性能首选):在项目根目录创建 websocket-server.php,内容如下:
require_once 'vendor/autoload.php';
use Swoole\WebSocket\Server;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
$server = new Server('0.0.0.0', 9503);
$server->set(['worker_num' => 4]);
$server->on('open', function (Server $server, Request $request) {
echo "Client {$request->fd} connected\n";
$server->push($request->fd, json_encode(['type' => 'welcome', 'ts' => time()]));
});
$server->on('message', function (Server $server, Frame $frame) {
$data = json_decode($frame->data, true);
if (isset($data['action']) && $data['action'] === 'broadcast') {
foreach ($server->connections as $fd) {
if ($server->isEstablished($fd)) {
$server->push($fd, json_encode(['type' => 'broadcast', 'payload' => $data['payload']]));
}
}
}
});
$server->on('close', function (Server $server, int $fd) {
echo "Client {$fd} disconnected\n";
});
$server->start();
运行命令:php websocket-server.php。确保系统已安装 Swoole 扩展(v5.0+),且端口 9503 在防火墙和宝塔/安全组中放行。
方法二(Ratchet,纯 PHP 兼容方案):执行 composer require cboden/ratchet,然后创建 bin/websocket 可执行脚本,写入启动逻辑——注意该方式需搭配 ReactPHP EventLoop,性能弱于 Swoole,但调试友好。
第二步:让 Symfony 7.0 与 WebSocket 服务互通
不能让 Symfony 直接调用 $server->push(),因为那是另一个进程的内存空间。必须引入中间层。
① 安装 Redis 驱动:composer require predis/predis,并在 config/packages/cache.yaml 中配置 Redis DSN。
② 在 WebSocket 服务中监听 Redis 频道:修改 websocket-server.php,在 $server->start() 前加入:
$redis = new Predis\Client('tcp://127.0.0.1:6379');
$pubsub = $redis->pubSub();
$pubsub->subscribe('symfony:notify', function ($msg) use ($server) {
$payload = json_decode($msg->getPayload(), true);
foreach ($server->connections as $fd) {
if ($server->isEstablished($fd)) {
$server->push($fd, json_encode($payload));
}
}
});
③ 在 Symfony 控制器中发布消息:
use Predis\Client;
final class NotifyController
{
public function __construct(private Client $redis)
{ }
#[Route('/api/push', methods: ['POST'])]
public function push(Request $request): Response
{
$data = json_decode($request->getContent(), true);
$this->redis->publish('symfony:notify', json_encode([
'type' => 'alert',
'content' => $data['message'] ?? 'No message',
'timestamp' => time()
]));
return new JsonResponse(['status' => 'sent']);
}
}
第三步:前端建立 WebSocket 连接并处理消息
在 Symfony Twig 模板中写入 JS 逻辑,不要用 ws://localhost:9503——生产环境必须用 Nginx 反向代理或 WSS。
const ws = new WebSocket('wss://your-domain.com/ws');
ws.onopen = () => { console.log('Connected to WebSocket server'); };
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'broadcast') {
document.getElementById('chat').innerHTML += `
}
};
ws.onerror = (err) => { console.error('WS error:', err); };
ws.onclose = () => { setTimeout(() => window.location.reload(), 3000); };
Nginx 配置关键段(否则浏览器会报错“Error during WebSocket handshake”):
location /ws {
proxy_pass http://127.0.0.1:9503;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
这一步做完后,前端即可收到来自 Symfony 控制器触发的实时推送消息。











