LoginSignup
17
19

More than 1 year has passed since last update.

Docker×Laravel×Redis×Laravel Echoを使ってリアルタイム通信する

Posted at

はじめに

Googleで検索すると、Laravelの公式ドキュメントや他の記事でLaravelとRedisを用いたWebSocket通信のやり方がたくさんでてきましたが、どれを試してなかなか上手くいかず苦戦したので、メモ代わりに記事に残します。各ツールの説明は省略します。検索した方が分かりやすい記事がたくさん出てくると思うので...。

環境

Docker Desktop for Mac 3.6.0
PHP 8.0.9
Laravel 8.40
redis-server 6.2.5
Laravel-echo-server 1.6.2
node 14.16.0
npm 7.8.0
composer 1.10.19

dockerでコンテナを構築

今回は下記の記事で作成したDokcer環境をベースに必要なものを追加しました。@ucan-lab さん、ありがとうございます。
【超入門】20分でLaravel開発環境を爆速構築するDockerハンズオン

プロジェクト構成

.
├── infra
│   ├── echo-server
│   │   └──Dockerfile
│   ├── mysql
│   │   ├── Dockerfile
│   │   └── my.cnf
│   ├── nginx 
│   │   └── default.conf
│   ├── php
│   │   ├── Dockerfile (この名前にするとファイル名の指定を省略できる)
│   │   └── php.ini
│   └── redis
│       └── data(ディレクトリです)
├── docker-compose.yml (この名前にするとファイル名の指定を省略できる)
└── backend
    └── Laravelをインストールするディレクトリ

各ファイルの中身

docker-compose.yml
version: "3.9"
services:
  app:
    build: ./infra/php
    volumes:
      - ./backend:/work
  web:
    image: nginx:1.20-alpine
    ports:
      - 8080:80
    volumes:
      - ./backend:/work
      - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf
    working_dir: /work
  db:
    build: ./infra/mysql
    ports:
      - 33060:3306
    volumes:
      - db-store:/var/lib/mysql
  echo-server:
    image: broadcast-echo-server
    build: ./infra/echo-server
    ports:
    - "6001:6001"
    command: laravel-echo-server start
    volumes:
    - ./backend:/work
    working_dir: /work
  redis:
    image: redis:latest
    ports:
      - 6379:6379
    volumes:
      - "./infra/redis/data:/data"
volumes:
  db-store:
./infra/echo-server/Dockerfile
FROM node:13.8-alpine
RUN npm install -g laravel-echo-server
WORKDIR /work
CMD ["laravel-echo-server", "start"]
./infra/mysql/Dockerfile
FROM mysql/mysql-server:8.0
ENV MYSQL_DATABASE=laravel_local \
  MYSQL_USER=phper \
  MYSQL_PASSWORD=secret \
  MYSQL_ROOT_PASSWORD=secret \
  TZ=Asia/Tokyo
COPY ./my.cnf /etc/my.cnf
RUN chmod 644 /etc/my.cnf
./infra/mysql/my.cnf
[mysqld]
# default
skip-host-cache
skip-name-resolve
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
secure-file-priv=/var/lib/mysql-files
user=mysql
pid-file=/var/run/mysqld/mysqld.pid
# character set / collation
character_set_server = utf8mb4
collation_server = utf8mb4_ja_0900_as_cs_ks
# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM
# Error Log
log-error = mysql-error.log
# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0
# General Log
general_log = 1
general_log_file = mysql-general.log
[mysql]
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
./infra/nginx/default.conf
server {
    listen 80;
    server_name example.com;
    root /work/public;
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    index index.php;
    charset utf-8;
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }
    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
    location ~ /\.(?!well-known).* {
        deny all;
    }
}
./infra/php/Dockerfile
FROM php:8.0-fpm-buster
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]
ENV COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer
COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer
RUN apt-get update && \
  apt-get -y install git unzip libzip-dev libicu-dev libonig-dev && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  docker-php-ext-install intl pdo_mysql zip bcmath
COPY ./php.ini /usr/local/etc/php/php.ini
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
RUN apt-get install -y nodejs
RUN npm install npm@latest -g
WORKDIR /work
./infra/php/php.ini
zend.exception_ignore_args = off
expose_php = on
max_execution_time = 30
max_input_vars = 1000
upload_max_filesize = 64M
post_max_size = 128M
memory_limit = 256M
error_reporting = E_ALL
display_errors = on
display_startup_errors = on
log_errors = on
error_log = /dev/stderr
default_charset = UTF-8
[Date]
date.timezone = Asia/Tokyo
[mysqlnd]
mysqlnd.collect_memory_statistics = on
[Assertion]
zend.assertions = 1
[mbstring]
mbstring.language = Japanese

Laravelをインストール

Dockerコンテナを立ち上げた後、Laravelをインストールします。
Laravelのインストールから環境構築までは下記記事と同様のやり方なので、参考にしてください。
【超入門】20分でLaravel開発環境を爆速構築するDockerハンズオン

Laravel-echo-serverの設定

初回コンテナ起動時、Laravel-echo-serverの初期設定がすんでないため、エラーが発生するので、初期設定を行います。

$ docker compose run echo-server laravel-echo-server init
? Do you want to run this server in development mode? Yes
? Which port would you like to serve from? 6001
? Which database would you like to use to store presence channel members? redis
? Enter the host of your Laravel authentication server. https://localhost
? Will you be serving on http or https? http
? Do you want to generate a client ID/Key for HTTP API? No
? Do you want to setup cross domain access to the API? No
? What do you want this config to be saved as? laravel-echo-server.json
Configuration file saved. Run laravel-echo-server start to run server.

上を実行したら、Laravelプロジェクト直下にlaravel-echo-server.jsonというファイルが作成されているので、以下のように書き換えます。

laravel-echo-server.json
{
    "authHost": "http://localhost",
    "authEndpoint": "/broadcasting/auth",
    "clients": [],
    "database": "redis",
    "databaseConfig": {
        "redis": {
            "host": "redis",
            "port": 6379
        },
        "sqlite": {
            "databasePath": "/database/laravel-echo-server.sqlite"
        }
    },
    "devMode": true,
    "host": null,
    "port": "6001",
    "protocol": "http",
    "socketio": {},
    "secureOptions": 67108864,
    "sslCertPath": "",
    "sslKeyPath": "",
    "sslCertChainPath": "",
    "sslPassphrase": "",
    "subscribers": {
        "http": true,
        "redis": true
    },
    "apiOriginAllow": {
        "allowCors": false,
        "allowOrigin": "",
        "allowMethods": "",
        "allowHeaders": ""
    }
}

必要なライブラリを追加

composer.json
"require": {
    // 他省略
    "predis/predis": "^1.1"
}

package.json
"dependencies": {
        // 他省略
    "laravel-echo": "^1.11.1",
    "laravel-echo-server": "^1.6.2",
    "socket.io": "^2.4.0",
    "socket.io-client": "^2.4.0",
}

各種設定

プロバイダーの設定

app/config/app.phpの以下の部分のコメントアウトを外す

App\Providers\BroadcastServiceProvider::class,

redisの設定

app/config/database.php
'redis' => [
        'client' => env('REDIS_CLIENT', 'predis'),
        'default' => [
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 0,
        ],
        'cache' => [
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 1,
        ],
        'options' => [
            'prefix' => env('REDIS_PREFIX', ''),
        ],
    ],

.envファイルの変更

.env
BROADCAST_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=redis
REDIS_CLIENT=predis
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_PREFIX=""

ここまででWebSocketによるリアルタイム通信を行えるようになりました。

実際に送ってみる

LaravelのEvent機能を使ってクライアント側にリアルタイムでデータを送信します。

サーバーサイドの準備

Eventの作成

php artisan make:event MessageRecieved
app/Events/MessageRecieved.php
<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class MessageReceived implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;


    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new Channel('test');
    }

    public function broadcastWith()
    {
        return [
            'data' => 'test',
        ];
    }
}

Event発火の準備

今回はルートにアクセスしたときにEventを発火させます。

routes/web.php
Route::get('/', function () {
    event(new \App\Events\MessageReceived());
    return view('welcome');
});

ワーカーの起動

Redisによる処理をキューで実行するのでワーカーを起動しておく

php artisan queue:work

クライアントサイドの準備

ビューの準備

ルートで表示するビューを以下のようにします。

resources/views/welcome.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>Laravel</title>
    <link rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
    <div id="app">
      test
    </div>
    <script src="{{ mix('js/app.js') }}"></script>
    <script src="http://{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script>
</body>
</html>

スクリプトの準備

bootstrap.jsに以下を追記します。

resources/js/bootstrap.js
import Echo from "laravel-echo";
window.io = require("socket.io-client");
window.Echo = new Echo({
    broadcaster: "socket.io",
    host: window.location.hostname + ":6001",
});

window.Echo.channel("test").listen("MessageReceived", e => {
    console.log("メッセージ受け取り成功!!!");
});

コンパイルする

npm install && npm run dev

実行

chromeのデベロッペーツールのConsole画面を開いて
http://127.0.0.7:8080 に接続すると
スクリーンショット 2021-08-18 23.26.36.png
無事、クライアントでメッセージを受け取れてます。

動かないとき

Dockerの再起動でうまくいく場合があります。
それでもうまくいかない場合は、Dockerのログを確認しましょう。

docker compose logs

さいごに

今回はメモ代わりということでコードをベタ貼りしていく形になりました。ちなみにPusherというサービスを使えば、超簡単にリアルタイム通信が可能です。Laravelの公式ドキュメントにも記載されているので、気になった方はぜひ。

17
19
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
19