Laravel WebSockets: подписка на частные каналы не работает

Программного обеспечения:

В websockets.php (полный файл) у меня естьlocal_cert а также local_pkустановка с моими сертификатами. Если я оставлю этот параметр пустым, я даже не смогу подключиться. Я также установилverify_peerк false, потому что в противном случае я тоже не смогу подключиться.

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'),
            'host' => '127.0.0.1',
            'port' => 6001,
            'scheme' => 'https',
            'curl_options' => [
                CURLOPT_SSL_VERIFYHOST => 0,
                CURLOPT_SSL_VERIFYPEER => 0,
            ]
        ],
    ],

Если я избавлюсь от параметров curl, я получаю пустое исключение Broadcast, как описано здесь.

bootstrap.js:

window.Pusher = require('pusher-js');
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: '7d23096ae0ab2d02d220',
    wsHost: window.location.hostname,
    wsPort: 6001,
    wssPort: 6001,
    encrypted: true,
    disableStats: true,
    auth: {
        headers: {
            'X-CSRF-TOKEN': window.App.csrfToken,
        },
    },
})

Это все, что я получил из журналов после запуска php artisan websockets:serve:

New connection opened for app key 7d23096ae0ab2d02d220.
Connection id 49092664.114416323 sending message {"event":"pusher:connection_established","data":"{\"socket_id\":\"49092664.114416323\",\"activity_timeout\":30}"}

Я должен получать сообщения о прослушивании / присоединении к каналам, отправке сообщений и т. Д. Но все это в данный момент не работает. У меня есть такие вещи, как:

Echo.private('notifications.' + this.user.id)
                .listen('UserNotificationSent', (e) => {
                    console.log(e)
                })

События: например, UserNotificationSent.php.

Конечно, внутри у меня также есть все остальное: каналы с аутентификацией и т. Д. Все работало локально на моей машине с более низкой версией Laravel (5.4). Но я недавно обновился до 5.8 и развернулся на сервере, и теперь я борюсь с этим.

Я также открыл проблему на github.

ВАЖНОЕ ОБНОВЛЕНИЕ На самом деле это не связано с развертыванием, у меня такая же проблема с моей локальной настройкой. Что интересно, при прослушивании каналов черезEcho.channel() работает, однако, .private()не работает. На Github (ссылка выше) я встретил парня, у которого была точно такая же проблема. Мы пока не нашли решения.

3 ответа

Решение

Я нашел проблему.

У меня было это в моем web.php:

Route::post('/broadcasting/auth', function (Illuminate\Http\Request $req) {
if ($req->channel_name == 'users') {
    return Broadcast::auth($req);
}
});

Я точно не помню, почему и когда я это добавил, но, вероятно, это было отсюда. По какой-то причине это не привело ни к каким ошибкам. Во всяком случае, я избавился от этого, и теперь он работает как шарм.

Это происходит из-за того, что порт 6001 зарезервирован в nginx на реальном сервере (объяснение внизу). Мне нужно было использовать обратный прокси на nginx, чтобы он работал, и использовал порт 6002 для веб-сокетов на реальном сервере.

В nginx (по запросу я добавил полный код nginx):

server {

  #The nginx domain configurations
  root /var/www/laravel/public;
  index index.html index.htm index.php index.nginx-debian.html;
  server_name example.com www.example.com;

  #WHAT YOU NEED IS FROM HERE...
  location / {
      try_files $uri $uri/ /index.php?$query_string;

      # "But why port 6000, 6002 and 433? Scroll at the bottom"

      proxy_pass                          http://127.0.0.1:6001;
      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  https;
      proxy_set_header X-VerifiedViaNginx yes;
      proxy_read_timeout                  60;
      proxy_connect_timeout               60;
      proxy_redirect                      off;

      # Specific for websockets: force the use of HTTP/1.1 and set the Upgrade header
      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;
 }
 #..UNTIL HERE - The rest are classic nginx config and certbot

 #The default Laravel nginx config
 location ~ \.php$ {
      try_files $uri =404;
      fastcgi_split_path_info ^(.+\.php)(/.+)$;
      fastcgi_pass unix:/run/php/php7.2-fpm.sock;
      fastcgi_index index.php;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      include fastcgi_params;
 }

 #SSL by certbot
 listen [::]:443 ssl ipv6only=on; # managed by Certbot
 listen 443 ssl; # managed by Certbot

 ssl                         on;
 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
 include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

 ssl_session_cache           shared:SSL:30m;
 ssl_protocols               TLSv1.1 TLSv1.2;

 # Diffie Hellmann performance improvements
 ssl_ecdh_curve              secp384r1;
}

Все, что подключается к вашему домену через TLS, будет проксироваться в локальную службу на порту 6001 в виде обычного текста. Это выгружает весь TLS (и управление сертификатами) на Nginx, сохраняя конфигурацию вашего сервера websocket как можно более простой и понятной.

Это также значительно упрощает автоматизацию с помощью Let's Encrypt, поскольку уже есть реализации, которые будут управлять конфигурацией сертификатов в вашем Nginx и перезагружать их при необходимости.- Источник - Маттиас Джениар

Настройка эха:

let isProduction = process.env.MIX_WS_CONNECT_PRODUCTION === 'true';

Vue.prototype.Echo = new LaravelEcho({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    wssHost: window.location.hostname,
    wssPort: isProduction ? 6002 : 6001,
    wsHost: window.location.hostname,
    wsPort: isProduction ? 6002 : 6001,
    disableStats: false,
    encrypted: isProduction,
    enabledTransports: ['ws', 'wss'],
    disabledTransports: ['sockjs', 'xhr_polling', 'xhr_streaming']
});

В websockets.php

'apps' => [
    [
        'id' => env('MIX_PUSHER_APP_ID'),
        'name' => env('APP_NAME'),
        'key' => env('MIX_PUSHER_APP_KEY'),
        'secret' => env('MIX_PUSHER_APP_SECRET'),
        'enable_client_messages' => false,
        'enable_statistics' => true,
    ],
],

// I kept them null but I use LetsEncrypt for SSL certs too.
'ssl' => [
   'local_cert' => null,
   'local_pk' => null,
   'passphrase' => null,
]

И broadcasting.php

'pusher' => [
     'driver' => 'pusher',
     'key' => env('MIX_PUSHER_APP_KEY'),
     'secret' => env('MIX_PUSHER_APP_SECRET'),
     'app_id' => env('MIX_PUSHER_APP_ID'),
     'options' => [
         'cluster' => env('MIX_PUSHER_APP_CLUSTER'),
         'encrypted' => env('MIX_WS_CONNECT_PRODUCTION'),
         'host' => '127.0.0.1',
         'port' => env('MIX_WS_CONNECT_PRODUCTION') ? 6002 : 6001,
         'scheme' => 'http'
     ],
 ],

Это был мой полный цикл, который заставил его работать. Надеюсь, это поможет.


Цитата из объяснения Алекса Боумы:

"Но почему порт 6000, 6002 и 433, что за бардак!?"

Я тебя слышу! Позвольте мне немного объяснить, надеюсь, после этого все будет иметь смысл.

Дело в том, что открытие порта на вашем сервере может быть выполнено только одним приложением за раз (технически это неверно, но давайте будем проще). Итак, если мы позволим NGINX прослушивать порт 6001, мы не сможем запустить наш сервер веб-сокетов также и на порту 6001, поскольку он будет конфликтовать с NGINX и наоборот, поэтому мы позволяем NGINX прослушивать порт 6002 и позволять ему прокси (NGINX - обратный прокси в конце концов) весь этот трафик на порт 6001 (сервер веб-сокетов) через простой http. Удаление SSL, чтобы серверу веб-сокетов не нужно было знать, как обрабатывать SSL.

Таким образом, NGINX будет обрабатывать всю магию SSL и перенаправлять трафик в обычном http на порт 6001 на вашем сервере, где сервер веб-сокетов прослушивает запросы.

Причина, по которой мы не настраиваем SSL в конфигурации websockets.php и определяем схему в нашем broadcasting.php как http и используем порт 6001, заключается в том, чтобы обойти NGINX и напрямую взаимодействовать с сервером веб-сокетов локально без необходимости использования SSL, что быстрее (и проще настраивать и поддерживать).

Используйте метод broadcastAs() с событием

public function broadcastAs()
{
    return 'UserNotificationSent';
}

И слушай это вот так.

.listen('.UserNotificationSent', function (e) {
    ....
});

Поставьте точку (.) Перед событием UserNotificationSent, чтобы прослушать его.

см. здесь https://laravel.com/docs/6.x/broadcasting

Другие вопросы по тегам