Как использовать Laravel с Socket.IO

Однажды передо мной встала задача: показывать список пользователей которые просматривают текущую старницу. И я стал думать как же это сделать. Часть меня (к счастью не самая сильная моя часть) хотела сделать "по быстрому" и забыть об этом. Другая же часть хотела сделать качественное решение, которое можно будет использовать в других разработках.

 

"Почему бы просто не использовать Pusher?"

Хороший вопрос.

В Laravel "изкоробки" вкюченна поддержка для Pusher API. Однако, хоть Pusher и кажется на первый взгляд хорошим решением, у него есть свои недостатки (вы видели их цены?).

Большинство статей с заголовком "Используем Websocket в Laravel" просто показывают как подключить Pusher к вашему проекту, и ни о какой настройке Websocket так и речи не идет (моя любимая часть, когда они говорят "и вот так же легко вы можете переключиться на использование socket.io").

Дайте нам безлимитное количество подключений!

Приступим. Я буду использовать Laravel/Homestead.

Для начала вам следует ознакомиться с Event Broadcasting в Laravel. Обратите особое внимание на информацию про:

  • ShouldBroadcast интерфейс
  • Включение Broadcast для маршрутов и использование route/channels.php для авторизации пользователей
  • Public Channels - публичные каналы.
  • Private Channels - приватные каналы.
  • Presence Channels - похожи на приватные каналы, но в них можно передавать дополнительные мета-данные, а так же получать список людей которые слушают этот канал.

 

 

Создаем свое событие

php artisan make:event MessagePushed

Устанавливаем Redis

Ранее я уже настраивал очереди с помощью Supervisor/Redis/Horizon, и в данном случае я поступил так же. Прочитайте документацию по Horizon, и настройте по ней свой проект.

Когда вы закончите настройку очередей, наше событие MessagePushed сможет их использовать. Так же убедитесь что вы правильно настроили ваш .env файл:

BROADCAST_DRIVER=redis
QUEUE_DRIVER=redis
(this is from the horizon setup actually, but we will need that for later)
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

Установим Laravel Echo Server

Вот мы и подошли к той части, когда мы собственно установим socket.io (который является частью laravel-echo-server). Полную документацию вы можете найти на Github проекта.

Перед установкой убедитесь что у вас используется:

  • Laravel 5.3+
  • Node 6.0+
  • Redis 3+

Выполните команду:

npm install -g laravel-echo-server

И затем выполните команду init, чтобы создать файл laravel-echo-server.json:

laravel-echo-server init

После этой команду в корне вашего проекта должен создаться файл laravel-echo-server.json с примерно таким содержимым:

{
  "authHost": "http://local-website.app",
  "authEndpoint": "/broadcasting/auth",
  "clients": [
    {
      "appId": "my-app-id",
      "key": "my-key-generated-with-init-command"
    }
  ],
  "database": "redis",
  "databaseConfig": {
    "redis": {},
    "sqlite": {
      "databasePath": "/database/laravel-echo-server.sqlite"
    },
    "port": "6379",
    "host": "127.0.0.1"
  },
  "devMode": false,
  "host": null,
  "port": "6001",
  "protocol": "http",
  "socketio": {},
  "sslCertPath": "",
  "sslKeyPath": "",
  "sslCertChainPath": "",
  "sslPassphrase": ""
}

 

На заметку: не забудьте добавить файл laravel-echo-server.json в ваш .gitignore, если вы собираетесь размещать ваш проект в публичном репозитории.

Запускаем Laravel Echo Server

Теперь мы готовы к запуску нашего собственного socket.io сервера:

laravel-echo-server start

(эту команду нужно выполнять в корне вашего проекта, там где лежит файл laravel-echo-server.json)

Все должно пройти хорошо, и сервис должен запуститься. (Если все прошло отлично, теперь вы можете добавить эту команду в запуск вашего Supervisor, чтобы он мог запускать и перезапускать сервис).

Откройте файл /etc/supervisor/conf.d/laravel-echo.conf (создайте новый) и пропишите в нем (не забудьте исправить пути и пользователя на правильные):

[program:laravel-echo]
directory=/var/www/my-website-folder
process_name=%(program_name)s_%(process_num)02d
command=laravel-echo-server start
autostart=true
autorestart=true
user=your-linux-user
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/my-website-folder/storage/logs/echo.log

Затем выполните следующие команды:

sudo supervisorctl stop all
sudo supervisorctl reread
sudo supervisorctl reload

И убедитесь что сервис запущен:

sudo supervisorctl status

Устанавливаем Laravel Echo и Socket.IO клиенты

Выполняем следующие команды:

npm install --save laravel-echo
npm install --save socket.io-client

И затем в вашем bootstrap.js (я использую VueJS) регистрируем Echo сервис:

import Echo from "laravel-echo"
window.io = require('socket.io-client');
// Have this in case you stop running your laravel echo server
if (typeof io !== 'undefined') {
  window.Echo = new Echo({
    broadcaster: 'socket.io',
    host: window.location.hostname + ':6001',
  });
}

Теперь вам стоит опять заглянуть в документацию по Broadcasting в Laravel. Из нее следует, что если в методе broadcastOn() в вашем событии вы возвращаете new PresenceChannel, то вы сможете слушать это событие из вашего JavaScript.

Вот конкретный пример:

1) Мы посылаем событие в Presence канал

public function broadcastOn()
{
  return new PresenceChannel('survey.' . $this->survey->id);
}

2) После этого событие попадает в channels.php и уже в нем мы проверяем может ли пользователь получить это событие (для Presence каналов мы должны возвращать массив с данными, а не просто false если пользователь не может получить доступ к этому каналу):

Broadcast::channel('survey.{survey_id}', function ($user, $survey_id) {
  return [
    'id' => $user->id,
    'image' => $user->image(),
    'full_name' => $user->full_name
  ];
});

3) Затем в нашем VueJS компоненте мы можем слушать этот канал в методе create():

listenForBroadcast(survey_id) {
  Echo.join('survey.' + survey_id)
    .here((users) => {
      this.users_viewing = users;
      this.$forceUpdate();
    })
    .joining((user) => {
      if (this.checkIfUserAlreadyViewingSurvey(user)) {
        this.users_viewing.push(user);
        this.$forceUpdate();
      }
    })
    .leaving((user) => {
      this.removeViewingUser(user);
      this.$forceUpdate();
    });
},

На этом в принципе все. Теперь вы можете реализовать свои собственные события, отправлять их в канал и получать о них информацию в JS.

Перевод статьи How to use Laravel with Socket.IO за авторством Adnan Sabanovic.

 

Опубликовано:

Категории: Статьи