はじめに
普段みなさんが使っているSNS等のアプリには、フォローしたユーザーがメッセージの送信や投稿をすると、リアルタイムで通知がされるようになっていると思います。
今では、SNSにおいてリアルタイム通信というのは、必須といえる機能となりUX(ユーザー体験)をよりよくしてくれます。
Webアプリにおけるリアルタイム通信といえば、 WebSocket や WebRTC が思い浮かびますよね。
ただ、私自身は WebSocket や WebRTC を使った開発経験はなく、ちょうどブログのネタも探していたので、 Laravel × Redis × Docker という構成で WebSocket による双方向通信を実装していこうと思います。
目標
ブラウザAからメッセージを送信すると、サーバーサイドでイベントが発火し、 WebSocket で接続されている別のブラウザBがデータを受信し、コンソールにメッセージが表示されるというところをゴールとして実装していきます。
完成形のコードはこちらのリポジトリの feature/websocket
ブランチにあります。
GitHub - TakanoriIchikawa/docker-laravel-websocket
Contribute to TakanoriIchikawa/docker-laravel-websocket development by creating an account on GitHub.
https://github.com/TakanoriIchikawa/docker-laravel-websocket
完成形のコードを確認したい方は、以下のコマンドでGitクローンしてください。
環境構築
まずは環境構築をしていきます。すでに Laravel が動作する環境があれば不要ですが、docker-compose.ymlやDockerfileの設定、コンテナの再ビルドは忘れないよう注意していください。
Laravel 環境の準備
1. Gitクローン
こちらのリポジトリから環境構築のためのコードを取得します。上述したリポジトリと同じです。
GitHub - TakanoriIchikawa/docker-laravel-websocket
Contribute to TakanoriIchikawa/docker-laravel-websocket development by creating an account on GitHub.
https://github.com/TakanoriIchikawa/docker-laravel-websocket
master
ブランチ → Dockerfileの設定とLaravelの準備まで完了しています
feature/websocket
ブランチ → WebSocketを使ったリアルタイム通信が実装完了しています
2. ディレクトリの移動
3. dockerコンテナのビルド
4. dockerコンテナの起動
5. laravelの初期設定
上記のコマンドを実行したら、 http://localhost にアクセスしてください。
以下の画面が表示されれば環境構築完了です。
Dokcer周りの設定
WebSocket通信を実現する上でポイントとなるDockerの設定ファイルを紹介します。
すでにLaravelが動作する環境があり、「Laravel 環境の準備」をやっていない方は、Dockerfileの追加やdocker-compose.ymlの調整をお願いします。
laravel-echo-serverのDockerfile
socket.ioで作られたLaravelでWebSocket通信(ブロードキャスト)するためのサーバー(Nodejs)です。
laravel-echo-server start
のコマンドで起動して、6001ポートでWebSocket接続を受けます。
php-fpmのDockerfile
Redisブロードキャスタを使用するので、 pecl
を使い phpredis
をインストールします。
Laravelで発火したイベントを受け取り、Redisのpub/sub機能を使い、laravel-echo-serverを介することでクライアント(ブラウザ)にデータを送ります。
d ocker-compose.yml
redisやecho-serverコンテナの設定を確認してください。
laravel-echo-serverの設定
初回コンテナ起動時には、laravel-echo-serverの設定が済んでいないので、エラーが発生してecho-serverのコンテナが起動できません。
以下のコマンドを実行して、初期設定をします。
設定は以下の通りです。
入力が終わると、Laravelプロジェクトの直下に laravel-echo-server.json
というファイルが生成されます。
生成されたファイルを編集します。 redis
の設定に以下を追記してください。
最終的には、以下のようになります。
以上で、laravel-echo-serverの設定は完了です。
dockerコンテナを確認すると、echo-serverのコンテナが問題なく起動されていると思います。
また、 laravel-echo-server
が正しく起動されると、 laravel-echo-server.lock
ファイルも生成されるので確認してください。
redisの設定
.env
を以下のように編集します。
./src/config/app.php
を開き、 App\Providers\BroadcastServiceProvider::class
のコメントアウトを外します。
特に設定を変更する必要はありませんが、 ./src/config/database.php
の Redis 周りの設定は以下のようになっています。
ライブラリのインストール
appコンテナ内でライブラリをそれぞれインストールをしてください。
appコンテナには、ルートディレクトリから以下のコマンドを実行することで接続できます。
laravel-echo
https://www.npmjs.com/package/laravel-echo
Laravelが提供しているJavaScriptのライブラリです。
laravel-echo-server がブロードキャストしたイベントを受け取ってくれます。
socket.io-client
https://www.npmjs.com/package/socket.io-client
クライアントからWebSocket通信でサーバーへ接続するために必要になります。
socket.io-client のバージョンが2系でないと、laravel-echo-server に接続できないようなので、バージョンを ^2.4.0
で指定します。
GitHubのイシューにも上がっています。2022年12月時点ではまだ解決していないようです。
Unable to connect with `laravel-echo-server` with latest `socket.io-client` · Issue #576 · tlaverdure/laravel-echo-server
Describe the bug Whenever I try to connect with laravel-echo-server with the latest socket.io-client and laravel-echo, it does not connect to the laravel-echo-server, however If I downgrade socket....
https://github.com/tlaverdure/laravel-echo-server/issues/576
これで、環境構築とWebSocket通信を実現するための準備ができました。
機能実装
クライアント(ブラウザ)からメッセージを送信すると、WebSocketで常時接続されているクライアント(ブラウザ)に対してイベントを介し、データ(メッセージ)送信する機能を実装します。
イベント
MessageEvent
を作成します。appコンテナで以下のコマンドを実行してください。
./src/app/Events/MessageEvent.php
が生成されるので、以下のように編集します。
Redisによる処理をキューで実行するには、ワーカーを起動する必要があります。
appコンテナで以下のコマンドを実行すると、ワーカーが起動します。
ルーティング
./src/routes/web.php
に以下のルーティングを追記してください。
それぞれ以下の処理をします
-
メッセージ送信フォームを表示する
-
ブラウザから送信されたメッセージを処理する
コントローラー
MessagesControllerを作成します。appコンテナで以下のコマンドを実行してください。
コントローラーの記述はとてもシンプルです。単純にビューを返す index()
とメッセージが送信された際にイベントを呼ぶ store()
になります。
実際にはメッセージをDBに保存する等の処理が入ると思いますが、大筋のところには影響ないので、割愛します。
ビュー
続いてビューファイルを用意します。 ./src/resources/views/messages/index.blade.php
を作成してください。
メッセージを送信するだけの味気ないものですが、WebSocket通信を確認する分には十分なので、このまま進めます。
クライアント側の記述
バックエンドでイベントが発生した際に、クライアント(ブラウザ)でイベントを受け取るための設定をします。
./src/resources/js/bootstrap.js
で事前にインストールしたライブラリを読み込みます。以下を追記してください。
そして、 ./src/resources/views/messages/index.blade.php
の </body>
の下にも追記します。
これでクライアント(ブラウザ)側の設定は完了です。
動作検証
コンパイル
appコンテナで、以下のコマンドを実行します。このコマンドを実行しないと Vite manifest not found at
とエラーが出てしまいます。
ワーカー起動
前述しましたが、Redisによる処理をキューで実行するためワーカーを起動します。
appコンテナで以下のコマンドを実行します。
ブラウザ確認
ブラウザを二つ開いてください。一つをブラウザA、もう一つをブラウザBとします。
ブラウザBでデベロッパーツールのコンソールを開きます。
ブラウザAからフォームにテキストを入力して、メッセージを送信してください。
すると、ブラウザBのコンソールにブラウザAで入力したメッセージが表示されます。
まとめ
最後まで記事を読んでいただきありがとうございます。初めての WebSocket を使った機能実装でしたので、いろいろと苦戦しました。
ただ、 WebSoket によるリアルタイム通信が成功した時は、苦労した分とても嬉しかったです。
次は実案件でも使えるようローカル環境ではなく、本番運用を想定したサーバー構築もしたいなと思っています。まぁぶっちゃけ、リアルタイム性が求められる案件は少なく日の目を浴びることはないかもですが…笑
では、また次回の記事で!