Laravel Echo结合Soketi无法广播事件:是否为配置问题?
问题需求
我希望在用户的文章动态更新完成后,广播FeedGenerated事件,让前端捕获该事件,进而允许用户手动刷新动态。目前FeedGenerated事件已被UpdateFeedArticles监听器监听,且监听器的日志记录功能正常,但Soketi未捕获到任何事件内容(无Docker日志输出)。
事件广播方式
FeedGenerated::broadcast($user);
FeedGenerated事件代码
<?php namespace App\Providers; use App\Models\User; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithBroadcasting; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class FeedGenerated implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels, InteractsWithBroadcasting; /** * 广播事件使用的队列连接名称 * * @var string */ public string $connection = 'redis'; /** * 广播任务所在的队列名称 * * @var string */ public string $queue = 'default'; /** * 创建事件实例 */ public function __construct(public User $user) { // } /** * 获取事件广播的频道 * * @return array<int, \Illuminate\Broadcasting\Channel> */ public function broadcastOn(): array { return [ new Channel("user"), ]; } }
UpdateFeedArticles监听器代码
<?php namespace App\Providers; use App\Providers\FeedGenerated; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; class UpdateFeedArticles { /** * 创建监听器实例 */ public function __construct() { // } /** * 处理事件 */ public function handle(FeedGenerated $event): void { \Log::debug('Event triggered', ['event' => $event, 'listener' => $this]); } }
Laravel Echo初始化代码
import Echo from 'laravel-echo'; import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Websocket = new Echo({ broadcaster: 'pusher', key: import.meta.env.VITE_PUSHER_APP_KEY, cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, wsHost: import.meta.env.VITE_PUSHER_HOST, wsPort: import.meta.env.VITE_PUSHER_PORT, wssPort: import.meta.env.VITE_PUSHER_PORT, forceTLS: false, encrypted: false, disableStats: true, enabledTransports: ['ws', 'wss'], }); Websocket.connector.pusher.connection.bind('connected', () => { console.log('✅ Soketi Connected!'); }); Websocket.connector.pusher.connection.bind('disconnected', () => { console.log('👋 Soketi Disconnected!'); }); Websocket.connector.pusher.connection.bind('failed', () => { console.log('❌ Soketi Connection Failed!'); });
docker-compose.yml配置
version: '3' networks: network_shapes: ipam: driver: default driver: bridge services: phpfpm: container_name: '${APP_NAME}' build: args: user: '${WWWUSER}' uid: '${WWWGROUP}' context: ./docker dockerfile: Dockerfile working_dir: /var/www/html volumes: - ./:/var/www/html - 'tmpfiles:/tmp' networks: - network_shapes depends_on: - mariadb restart: unless-stopped soketi: image: 'quay.io/soketi/soketi:latest-16-alpine' environment: SOKETI_DEBUG: '${SOKETI_DEBUG:-1}' SOKETI_METRICS_SERVER_PORT: '${SOKETI_METRICS_SERVER_PORT}' ports: - '${SOKETI_PORT:-6001}:6001' - '${SOKETI_METRICS_SERVER_PORT:-9601}:9601' networks: - network_shapes depends_on: - redis nginx: image: ${NGINX_IMAGE} networks: - network_shapes ports: - '127.0.0.1:${APP_PORT}:80' volumes: - ./docker/nginx/tkt.conf:/etc/nginx/conf.d/default.conf - ./:/var/www/html - 'tmpfiles:/tmp' links: - phpfpm depends_on: - mariadb restart: unless-stopped mongo: ... redis: image: 'redis:latest' ports: - '127.0.0.1:${REDIS_PORT:-6379}:6379' volumes: - 'redis_volume:/data' networks: - network_shapes healthcheck: test: [ "CMD", "redis-cli", "ping" ] retries: 3 timeout: 5s restart: unless-stopped mariadb: ... mailhog: ... volumes: tmpfiles: driver: local mariadb_volume: driver: local redis_volume: driver: local
Nginx tkt.conf配置
server { listen 80; server_name localhost www.localhost .localhost *.localhost; index index.php index.html; error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log; root /var/www/html/public; client_max_body_size 100M; location /mailhog/ { proxy_pass http://mailhog:8025/; 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-Client-Verify SUCCESS; proxy_read_timeout 1800; proxy_connect_timeout 1800; chunked_transfer_encoding on; proxy_set_header X-NginX-Proxy true; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_http_version 1.1; proxy_redirect off; proxy_buffering off; } location / { try_files $uri $uri/ /index.php?$query_string; gzip_static on; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass phpfpm:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } location /websocket/ { proxy_pass http://127.0.0.1:6001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; proxy_read_timeout 86400; # 根据需要调整 } }
当前运行情况
执行事件广播命令
docker exec -t shapppes php artisan feed:generate 01h739jr4vk8pq3yefsp22c964 Scoring articles for Sonny Stroman... Feed created.
Horizon日志
docker exec -t shapppes php artisan horizon Horizon started successfully. 2023-08-07 13:04:56 App\Actions\Article\Feed\GenerateFeed .......... RUNNING 2023-08-07 13:04:57 App\Actions\Article\Feed\GenerateFeed .... 899.66ms DONE 2023-08-07 13:04:57 App\Providers\FeedGenerated .................... RUNNING 2023-08-07 13:04:57 App\Providers\FeedGenerated ............... 61.82ms DONE
Redis查询结果(Laravel Telescope可见)
eval -- Push the job onto the queue... redis.call('rpush', KEYS[1], ARGV[1]) -- Push a notification onto the "notify" queue... redis.call('rpush', KEYS[2], 1) 2 queues:default queues:default:notify {"uuid":"bf2e429e-7702-4758-bedd-d072914a3215","displayName":"App\\Providers\\FeedGenerated","job":"Illuminate\\Queue\\CallQueuedHandler@call","maxTries":null,"maxExceptions":null,"failOnTimeout":false,"backoff":null,"timeout":null,"retryUntil":null,"data":{"commandName":"Illuminate\\Broadcasting\\BroadcastEvent","command":"O:38:\"Illuminate\\Broadcasting\\BroadcastEvent\":14:{s:5:\"event\";O:27:\"App\\Providers\\FeedGenerated\":1:{s:4:\"user\";O:45:\"Illuminate\\Contracts\\Database\\ModelIdentifier\":5:{s:5:\"class\";s:15:\"App\\Models\\User\";s:2:\"id\";s:26:\"01h739jr4vk8pq3yefsp22c964\";s:9:\"relations\";a:4:{i:0;s:4:\"page\";i:1;s:14:\"page.bookmarks\";i:2;s:10:\"page.image\";i:3;s:4:\"tags\";}s:10:\"connection\";s:5:\"mysql\";s:15:\"collectionClass\";N;}}s:5:\"tries\";N;s:7:\"timeout\";N;s:7:\"backoff\";N;s:13:\"maxExceptions\";N;s:10:\"connection\";N;s:5:\"queue\";N;s:15:\"chainConnection\";N;s:10:\"chainQueue\";N;s:19:\"chainCatchCallbacks\";N;s:5:\"delay\";N;s:11:\"afterCommit\";N;s:10:\"middleware\";a:0:{}s:7:\"chained\";a:0:{}}"},"telescope_uuid":"99d54a11-c8fc-489f-8676-a575ab0466bf","id":"bf2e429e-7702-4758-bedd-d072914a3215","attempts":0,"type":"broadcast","tags":["App\\Models\\User:01h739jr4vk8pq3yefsp22c964"],"silenced":false,"pushedAt":"1691413497.6378"}
异常情况
Soketi容器无任何日志输出,未捕获到广播事件。
排查与解决建议
检查Soketi环境变量配置
- 确保
.env中BROADCAST_DRIVER=pusher,且PUSHER_APP_KEY、PUSHER_APP_ID、PUSHER_APP_SECRET与Soketi配置一致 - Soketi容器需设置
SOKETI_AUTH_HOST为Laravel服务地址(如http://phpfpm,因为同属Docker网络) - 开启Soketi调试模式:
SOKETI_DEBUG=1,查看详细日志
- 确保
修正事件广播频道配置
- 当前
broadcastOn返回的是公共频道Channel("user"),若需针对单个用户,应使用PrivateChannel("user.{$this->user->id}"),并确保前端订阅对应频道 - 前端需添加频道订阅代码:
Websocket.channel('user') // 若改为私有频道则用.private('user.xxx') .listen('FeedGenerated', (e) => { console.log('Feed更新完成', e); // 触发手动刷新逻辑 });
- 当前
修正Nginx代理配置
- 当前Nginx的
/websocket/代理指向127.0.0.1:6001,但在Docker网络中应指向soketi:6001 - 调整代理配置:
location /websocket/ { proxy_pass http://soketi:6001; 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; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 86400; }
- 当前Nginx的
检查Laravel广播配置
- 确保
config/broadcasting.php中pusher配置正确:'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => env('PUSHER_APP_CLUSTER'), 'host' => env('PUSHER_HOST', 'soketi'), 'port' => env('PUSHER_PORT', 6001), 'scheme' => 'http', 'encrypted' => false, 'useTLS' => false, ], ],
- 确保
验证队列与广播任务
- 确保Horizon或队列 worker 正在运行,且处理
default队列的任务 - 检查
FeedGenerated事件的broadcastUsing或队列配置是否正确指向Redis
- 确保Horizon或队列 worker 正在运行,且处理
内容的提问来源于stack exchange,提问作者Gaël Duval
相关产品推荐
相关产品推荐

