带有独立服务器的 Laravel Websockets

Posted

技术标签:

【中文标题】带有独立服务器的 Laravel Websockets【英文标题】:Laravel Websockets with separate servers 【发布时间】:2022-01-08 15:25:42 【问题描述】:

我正在尝试用多个服务器实现 Laravel Websockets。

我有一个应用服务器和一个队列工作服务器正在运行。我尝试从 Queue Worker 服务器广播我的通知,但我收到了

lluminate\Broadcasting\BroadcastException: Pusher 错误: 。在/home/forge/my-app/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php:128

在 App Server 的 Network 选项卡中,我可以确认它已连接到 websocket。我使用了alex bouma's post 并设置了反向代理。 如果我在 App Server 中广播,它可以工作

server 
    listen 6002 ssl http2;
    listen [::]:6002 ssl http2;
    server_name example.com;
    index.php

    location / 
        proxy_pass             http://127.0.0.1:6001;
        proxy_read_timeout     60;
        proxy_connect_timeout  60;
        proxy_redirect         off;
        
        # Allow the use of websockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    

App Server 和 Queue Worker 位于专用网络中,可以相互连接。但是,当我尝试从 Queue Worker 服务器进行广播时,它不起作用。

我很难理解如何让它发挥作用。

我是否还必须在队列工作程序上运行php artisan websockets:serve?如果是这样,我需要给--host=private-ip标志吗?

在我的broadcasting.php 中,我已将应用服务器的私有 ip 添加为 PUSHER_ENDPOINT_HOST 用于 Queue Worker 的环境。这是正确的吗?

'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'),
         'encrypted' => in_array(config('app.env'), ['production', 'staging']),
         'host' => env('PUSHER_ENDPOINT_HOST', '127.0.0.1'),
         'port' => 6001,
         'scheme' => 'http'
      ],
],

更新:我按照@KamleshPaul 的建议进行了更改。下面是我所有代码的样子。

我在应用服务器和工作服务器中都有这些配置。我在两者上都运行php artisan websockets:serve,并且App Server 成功连接到套接字。但是,队列工作人员不会发送通知。

php artisan websockets:serve 均显示:“正在端口 6001 上启动 WebSocket 服务器...”

但它似乎仍然不起作用。 (顺便说一句,我使用的是 Laravel Forge,允许的端口是:

应用:6001、6002、433、22

工人:6001、6002、22

.env

PUSHER_APP_ID=aaa
PUSHER_APP_KEY=bbb
PUSHER_APP_SECRET=ccc
PUSHER_APP_CLUSTER=mt1
PUSHER_HOST=socket.my_domain.com
PUSHER_PORT=433
PUSHER_SCHEME=https

VITE_PUSHER_APP_KEY=$PUSHER_APP_KEY
VITE_PUSHER_APP_CLUSTER=$PUSHER_APP_CLUSTER
VITE_PUSHER_HOST=$PUSHER_HOST
VITE_PUSHER_SCHEME=PUSHER_SCHEME
VITE_PUSHER_PORT=PUSHER_PORT

echo.js

window.Echo = 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 || 443,
      forceTLS: true,
      disableStats: true,
      scheme: import.meta.env.VITE_PUSHER_SCHEME,
      enabledTransports: ["ws", "wss"],

广播.php

'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'),
        'encrypted' => true,
        'host' => env('PUSHER_HOST'),
        'port' => env('PUSHER_PORT'),
        'scheme' => env('PUSHER_SCHEME')
    ],
],

socket.my_domain.com的nginx

# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/socket.my_domain.com/before/*;

server 
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name socket.my_domain.com;
    server_tokens off;
    root /home/forge/socket.my_domain.com/public;

    # FORGE SSL (DO NOT REMOVE!)
    ssl_certificate /etc/nginx/ssl/socket.my_domain.com/1262458/server.crt;
    ssl_certificate_key /etc/nginx/ssl/socket.my_domain.com/1262458/server.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_dhparam /etc/nginx/dhparams.pem;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    # FORGE CONFIG (DO NOT REMOVE!)
    include forge-conf/socket.my_domain.com/server/*;

    location / 
        proxy_pass             http://127.0.0.1:6001;
        proxy_read_timeout     60;
        proxy_connect_timeout  60;
        proxy_redirect         off;
    
        # Allow the use of websockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    

    location = /favicon.ico  access_log off; log_not_found off; 
    location = /robots.txt   access_log off; log_not_found off; 

    access_log off;
    error_log  /var/log/nginx/socket.my_domain.com-error.log error;

    error_page 404 /index.php;

    location ~ \.php$ 
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    

    location ~ /\.(?!well-known).* 
        deny all;
    


# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/socket.my_domain.com/after/*;

【问题讨论】:

你需要在broadcasting.php中设置正确的host,portscheme 假设我有独立运行的 AppServer 10.0.0.1:6001 (https)。还有 QueueServer 10.0.03。正确的 hostportscheme 是什么? 我不认为在端口6001 中可以启用ssl?,通常我在子域中使用端口433,例如socket.appname.com 我的应用服务器块有端口 443。 6001 是 6002 端口的反向代理。你之前在多个实例中设置了 laravel websockets 吗? 是的,我的 websockets 是独立的 nginx 配置 【参考方案1】:

根据laravel websockets doc

创建一个子域socket.yourapp.tld 然后像这样创建一个 nginx 配置

server 
  listen        443 ssl;
  listen        [::]:443 ssl;
  server_name   socket.yourapp.tld;

  # Start the SSL configurations
  ssl                  on;
  ssl_certificate      /etc/letsencrypt/live/socket.yourapp.tld/fullchain.pem;
  ssl_certificate_key  /etc/letsencrypt/live/socket.yourapp.tld/privkey.pem;

  location / 
    proxy_pass             http://127.0.0.1:6001;
    proxy_read_timeout     60;
    proxy_connect_timeout  60;
    proxy_redirect         off;

    # Allow the use of websockets
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  

那么在broadcasting.php你需要改变这些

'host' => socket.yourapp.tld,
'port' => 433,
'scheme' => 'https'

同样适用于 javascript

export default new Echo(
  broadcaster: "pusher",
  key: "key",
  wsHost: process.env.REACT_APP_WS_HOST,
  wsPort: process.env.REACT_APP_WS_PORT || 443,
  forceTLS: process.env.REACT_APP_WS_PORT === 433,
  disableStats: true,
  enabledTransports: ["ws", "wss"],
  authorizer: (channel, options) => 
    return 
      authorize: (socketId, callback) => 
        axios
          .post(
            `$process.env.REACT_APP_MARKETPLACE_URLbroadcasting/auth`,
            
              socket_id: socketId,
              channel_name: channel.name,
            ,
            
              headers: 
                Authorization: `Bearer $token`,
              ,
            
          )
          .then((response) => 
            callback(false, response.data);
          )
          .catch((error) => 
            callback(true, error);
          );
      ,
    ;
  ,

参考链接https://beyondco.de/docs/laravel-websockets/basic-usage/ssl#usage-with-a-reverse-proxy-like-nginx

【讨论】:

感谢您的回复。您也可以分享您的回声配置吗?我只是设置了子域方法,但仍然无法使其工作。您是否在队列工作服务器上运行php artisan websockets:serve?如果是这样,你会添加任何标志吗? @senty。如果您有任何问题,请告诉我,并确保您也重新启动您的服务人员 谢谢 - 您是只在应用服务器上运行 php artisan websockets:serve 还是在队列工作者中运行? queue worker 是要求。在 websocket 中运行事件我有两个设置 我更新了 OP 并放置了我所有的配置。你能看一下吗?【参考方案2】:

我终于明白了。下面的这些设置对我有用。希望它能为您节省一些时间:)

我决定在子域方法上使用反向代理,例如 described in the docs。

对于子域的 nginx,与默认的 Laravel nginx 配置唯一不同的是location / 。只需复制您的 Laravel 应用程序的 nginx 配置,更改 server_name 并替换 location / 即可。

window.Echo = 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 || 443,
     forceTLS: true,
     disableStats: true,
     scheme: import.meta.env.VITE_PUSHER_SCHEME,
     enabledTransports: ["ws", "wss"],
);

// broadcasting.php

'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'),
        'encrypted' => true,
        'host' => env('PUSHER_HOST'),
        'port' => env('PUSHER_PORT'),
        'scheme' => env('PUSHER_SCHEME'),
    ],
],

// websockets.php

'apps' => [
    [
        'id' => env('PUSHER_APP_ID'),
        'name' => env('APP_NAME'),
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'host' => env('PUSHER_HOST'),
//        'capacity' => null,
        'enable_client_messages' => false,
        'enable_statistics' => false,
    ],
],

// .env file

PUSHER_APP_ID=HIDDEN_ID
PUSHER_APP_KEY=HIDDEN_KEY
PUSHER_APP_SECRET=HIDDEN_SECRET
PUSHER_APP_CLUSTER=mt1
PUSHER_HOST=socket.example.com;
PUSHER_PORT=6001
PUSHER_SCHEME=http

VITE_PUSHER_APP_KEY=$PUSHER_APP_KEY
VITE_PUSHER_APP_CLUSTER=$PUSHER_APP_CLUSTER
VITE_PUSHER_HOST=$PUSHER_HOST
VITE_PUSHER_SCHEME=https
VITE_PUSHER_PORT=433

确保在两种情况下都运行 php artisan websockets:serve:在我的情况下 - 应用程序和工作服务器。

确保您的应用服务器上允许使用端口 6001

就我而言,如果我 ssh 进入服务器并写入 php artisan websockets:serve,它不会像在本地那样显示即将到来的消息或连接,因此请注意。

在 App Server 中,要连接 Laravel Websockets 仪表板,只需使用空端口:


一些资源:

Up and running with Laravel Websockets + Laravel Websockets on Forge

https://blog.codecourse.com/laravel-websockets-on-forge/

【讨论】:

以上是关于带有独立服务器的 Laravel Websockets的主要内容,如果未能解决你的问题,请参考以下文章

websocke

带有服务器端的 Laravel 5 数据表

websocke前世今生

websocke 在线测试地址

websocke 在线测试地址

带有 Redis 的 Laravel 广播无法工作/连接?