Laravelでソケット通信をやってみます。
ソケット通信って?
主にリアルタイムなデータの送受信に使用されるもので、チャットアプリなどに用いられるものです。チャットアプリのメッセージは、画面を更新しなくても常に最新のものが表示されていきますよね。
おなじみのHTTP通信では、リクエストが送られたタイミングでレスポンスが返ってくるので、ユーザーが画面を更新しない限り最新の情報が表示されません。毎秒ごとにリクエストを発行する…というのは現実的ではないですよね。
そこで必要になるアプローチがソケット通信になります。
サーバーを常時監視し、アクションがあれば更新、といったことが可能になります。
Laravel WebSocketについて
ソケット通信を実装する一つの方法として、Pusherを用いる方法があります。Pusherでアカウントを作成して、Pusherサーバーを利用しての実装となります。
しかしなんと、Laravelは「Laravel WebSocket」により、自身でソケット通信を行うことが可能です。つまり、Pusherでアカウントを作成する必要はありません。Laravel自身がソケット通信のサーバーにもなるということです。
今回は、このLaravel WebSocketを用いて、Laravelでのソケット通信をやってみたいと思います。
やりたいこと
Laravel環境を2つ用意します。1つがバックエンド・1つがフロントという構成です。
バックエンド側のとあるアクションにPOSTされる度に、フロント側がリアルタイム(=画面更新なし)で表示を更新していく、というサンプルを作成します。
1.Dockerで環境作成
DockerでLaravel環境を2つ作成します。Dockerfileの内容など細かい部分は省きますので、ある程度お好みで作成して頂ければと思います。
バックエンド側
以下の要件を満たすLaravel環境を作成します。
- DBが使える
- ポート6001を許可する(ソケット通信がポート6001を使用するため)
ports:
- 8080:80
- 6001:6001
フロント側
- node及びnpmが使える
2.実装準備
dockerをビルド及び起動したら、まずは実装準備(composerなど)をします。
バックエンド側の実装準備
バックエンド側のコンテナに入り、下記でLaravelプロジェクトを作成
composer create-project laravel/laravel socket-test-back
socket-test-back/
で、下記を実行
composer require beyondcode/laravel-websockets
composer require pusher/pusher-php-server "~3.0"
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"
php artisan migrate
.env
のPUSHER情報を編集 ※PUSHER_APP_ID、PUSHER_APP_KEY、PUSHER_APP_SECRETは任意の名前
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=sampleId
PUSHER_APP_KEY=sampleKey
PUSHER_APP_SECRET=sampleSecret
PUSHER_APP_CLUSTER=mt1
config/broadcasting.php
の修正 ※Pusherの向き先を自身(localhost)にするイメージ
'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,
'useTLS' => false,
'host' => 'localhost',
'port' => 6001,
'scheme' => 'http',
],
],
以上で、バックエンド側の実装準備は完了になります。
フロント側の実装準備
フロント側のコンテナに入り、下記でLaravelプロジェクトを作成
composer create-project laravel/laravel socket-test-front
必要なものをインストール
npm install pusher-js
npm install laravel-echo
resource/js/bootstrap.js
の該当箇所のコメントアウトを外して、下記のようにする
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
forceTLS: false,
wsHost: 'localhost',
wsPort: 6001,
});
.env
のPUSHER情報を編集 ※PUSHER_APP_KEYとPUSHER_APP_CLUSTERをバックエンド側に合わせる
PUSHER_APP_ID=
PUSHER_APP_KEY=sampleKey
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
npm起動
npm install
npm run dev
以上で、フロント側の実装準備は完了になります。
3.実装
ソケットを使用する準備は完了したので、実装していきます。
バックエンド側の実装
以下実行して、SampleEventとSampleControllerを作成
php artisan make:event SampleEvent
php artisan make:controller SampleController
修正する
<?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 SampleEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public $comment;
public function __construct($comment)
{
$this->comment = $comment;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('sample-channel');
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Events\SampleEvent;
class SampleController extends Controller
{
public function fireChannel(Request $request)
{
SampleEvent::dispatch($request->comment);
return response()->json(
["status" => 200]
);
}
}
routes/api.phpを修正
Route::controller(SampleController::class)->group(function () {
Route::post('/sample/fire_channel', 'fireChannel');
});
これでバックエンド側の実装は完了しました。/sample/fire_channel
にPOSTすることで、チャネルsample-channel
にブロードキャストされます。
フロント側の実装
適当な画面を作成し、bladeを以下のように修正します。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TEST</title>
</head>
<body>
<div id="container"></div>
</body>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="/js/app.js"></script>
<script>
{{-- チャンネルを監視 イベント検知すると処理実行 --}}
window.Echo.channel('sample-channel')
.listen('SampleEvent', (e) => {
$("#container").append("<div>" + e.comment + "</div>");
});
</script>
</html>
これでフロント側の実装は完了しました。上記では、sample-channel
にブロードキャストされる度に、POSTされたcomment
を表示していくということをしています。
4.検証
バックエンド側で以下を実行し、ソケットサーバーを起動します。
php artisan websockets:serve
フロント側で作成した画面(ここでは/test
にしてます)を開いた状態で、Postmanで/sample/fire_channel
にPOSTしていきます。
ブラウザの画面更新なしで、情報が追加されていってますね!
まとめ
いかがでしたでしょうか?
今回はソケット通信を検証するためだけのサンプルとして、簡易にPOST値を表示するだけのものを作成しました。実際の開発では、イベント検知の度にDBを取得しにいって、リアルタイムで最新の情報を表示するといったことが多いかと思います。
ソケット通信を活用して、ユーザビリティ向上を図りましょう!
告知
最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを募集しております。
私は元々、自社開発企業にて開発を行っていましたが、自分の可能性を広げるべく、受託開発メインのイーディーエーに転職を決意しました。
イーディーエーに入社を決めた理由は、以下の3点です。
・多種多様な案件がある
・様々なことに挑戦できる
・かつフルリモートで勤務できる
様々な案件に挑戦し、スキルを身に付けたい。そしてフルリモートで仕事以外の時間も充実させたいという方は、ぜひ採用情報ページに足を運んでいただければと思います。
https://eda-inc.jp/recruit/
みなさまからのご応募をお待ちしております。