pusher.comを使わずに、laravel-echo-server(WebSocketサーバ)@さくらVPSで、外部サービスを使わずにリアルタイム通知してみた。
参考URL:
マグロと寿司とWebSocket。Laravel+Vue.jsで簡易的なチャットを作る。
laravelオススメpusher.comを使った方が楽だけど、無料枠だと同時接続数が100人までなので、自前でWebSocketサーバ(laravel echo server)を立ててみた。
1GBのVPSだと、同時接続数はどのくらいまで行けるのだろうか…。
やる事リスト
a, laravel-echo-serverはnode.jsで作られているので、node.jsをインストールする。
b, redis(Key-Value型のNoSQLデータベース)は、データ保持はせずに、PUBLIC(配信)/SUBCRIBE(購読)の機能だけ使う
c, predisは、phpからredisを使えるようにするライブラリ
d, laravel-echo-server.json(WebSocketサーバの設定ファイル)を記述
e, redisサーバ起動してから、laravel-echo-server起動
f, laravel-echo-server(WebSocketサーバ)のポート6001を開放しておく
g, フロント側(laravel-echo)は、pusher.comと大体同じだろ!と思いきや、チャネル名にlaravel_database_ というプレフィックスが付いていたり
イベント名の前にドットの一文字が必要だったり、微妙に違う!
1, 色々とインストール&サーバ起動
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# node.js yum install -y nodejs # redis(laravel echo serverに必要なKVSなデータベース) yum install -y redis # Predis(PHPのRedisクライアント)、laravelプロジェクトのパスまでcdしてから composer require predis/predis # redisサーバを起動 systemctl start redis.service ps -ef | grep redis redis 16734 1 0 12:25 ? 00:00:00 /usr/bin/redis-server 127.0.0.1:6379 root 16745 16321 0 12:25 pts/0 00:00:00 grep --color=auto redis # WebSocketサーバ(laravel-echo-server)をnpmインストール(npm= node package manager) npm install -g laravel-echo-server # WebSocketクライアントもインストール npm install --save laravel-echo socket.io-client # プロジェクトフォルダ直下に、WebSocketサーバの設定ファイルを生成する。基本的にはENTER連打でOK。devModeだけYESにしておいた方が良い。 laravel-echo-server init # HTTPSの場合は、SSL証明書のパスが聞かれる。kusanagiの場合は "sslCertPath": "/etc/letsencrypt/live/ドメイン名/cert.pem", "sslKeyPath": "/etc/letsencrypt/live/ドメイン名/privkey.pem", # WebSocketサーバを起動 # redisサーバを起動してないと、エラーを吐き続けるので注意! laravel-echo-server start L A R A V E L E C H O S E R V E R version 1.5.5 Starting server... ✔ Running at localhost on port 6001 ✔ Listening for http events... ✔ Listening for redis events... Server ready! |
laravel-echo-server(WebSocket)用に、6001ポートを開放しておく(OS再起動してもリセットされないようにしておく)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# firewall-cmd --add-port=6001/tcp --zone=public --permanent success # firewall-cmd --reload success # firewall-cmd --list-all public (active) target: default icmp-block-inversion: no interfaces: eth0 sources: services: dhcpv6-client http ssh https ports: 6001/tcp protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules: |
サーバ環境は構築できたので、laravel側のコーディング(過去記事を参照)
1, laravelがredisを使うようにする
.envファイルを1ヶ所修正(log->redis)、REDIS_CLIENT=predisは追記
BROADCAST_DRIVER=redis
REDIS_CLIENT=predis
config/databases.phpを見るとphpredisがデフォになっているので、インストールしたpredisを使うように修正。しないとエラーになった。
“Please make sure the PHP Redis extension is installed and enabled.”
2, フロント周り(受信側)を作る。送信側はpusherの時と全く同じでOK!
resources/js/bootstrap.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/* 標準のpusherは使わない! 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, encrypted: true }); */ //for Echo // websocketサーバが、httpsなのを忘れずに! import Echo from 'laravel-echo'; window.io = require('socket.io-client'); window.Echo = new Echo({ broadcaster: 'socket.io', host: 'https://' + window.location.hostname + ':6001' }); //購読するチャネルの設定 window.Echo.channel('my-channel') .listen('.my-event', (e) => { console.log(e); }); |
これで、pusher.comからローカルのlaravel echo serverに移行出来るはず!と思ったけど、通知が来ないな。
受信側(ブラウザ)は、ちゃんと接続出来ているっぽい。
Request URL: wss://localhost:6001/socket.io/?EIO=3&transport=websocket
Request Method: GET
Status Code: 101 Switching Protocols
じゃあ、送信側がダメなのか?
redisにデータが無い…。
1 2 3 4 |
# redis-cli 127.0.0.1:6379> keys * (empty list or set) 127.0.0.1:6379> |
そもそも、publish(発行)/subscribe(購読)の機能しか使わないから、データ無しが当たり前か。
手動で動かしてみよう。
購読状態で待機
1 2 3 4 5 6 |
# redis-cli 127.0.0.1:6379> SUBSCRIBE my_channel Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "my_channel" 3) (integer) 1 |
別のSSHで接続して発行する
1 2 3 |
# redis-cli 127.0.0.1:6379> PUBLISH my_channel "Hello! This is clientB" (integer) 2 |
購読状態で待機している方のSSHコンソールに表示された!
リアルタイムチャットな感じだ。
1 2 3 |
1) "message" 2) "my_channel" 3) "Hello! This is clientB" |
laravel-echo-server.jsonの”devMode”: trueにして、コンソールに表示してみたら、チャネル名に laravel_database_ というプレフィックスが付いている!?
1 2 |
Channel: laravel_database_my-channel Event: my-event |
config/database.phpに記述してあるのか…。余計な真似を!
プレフィックスは空欄にしておこう
1 2 3 4 5 6 7 8 9 |
'redis' => [ 'client' => env('REDIS_CLIENT', 'phpredis'), 'options' => [ 'cluster' => env('REDIS_CLUSTER', 'redis'), // 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), 'prefix' => '', ], |
う~ん、チャネル名とイベント名は合致するようになったけど、ブラウザのconsole.logにまだ出ない。なんでなんだろうか・・・。
1 2 3 |
[13:31:36] - 5LFrInjfowL_c0yWAAAC joined channel: my-channel Channel: my-channel Event: my-event |
そうだ、チャネル名の前に、ドットをつける必要があるんだったわ。円マーク(クラスの名前空間)と同じ発想?でもpusher.comの時には無しでも行けたのに~!
これで自前でリアルタイム通信出来るようなった。
1 2 3 4 5 |
//購読するチャネルの設定 window.Echo.channel('my-channel') .listen('.my-event', (e) => { console.log(e); }); |
サーバの再起動した時に、redisサーバとlaravel-echo-serverが自動起動するように設定。
redisはサービスなので簡単。
1 2 3 4 5 6 7 8 |
#systemctl list-unit-files -t service | grep redis redis-sentinel.service disabled redis.service disabled #systemctl enable redis Created symlink from /etc/systemd/system/multi-user.target.wants/redis.service to /usr/lib/systemd/system/redis.service. # systemctl is-enabled redis enabled |
pm2の方は、reboot時に自動起動するコマンド(pm2 startup)があるので、rootで実行。各OS毎に自動起動の方法が違うので、これはありがたい。
pm2 saveコマンドで保存してから、rebootしてもwebsocket通信出来た!
https://qiita.com/ikemura23/items/68fb61b16c6752daa7e8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# pm2 startup [PM2] Init System found: systemd Platform systemd Template [Unit] Description=PM2 process manager Documentation=https://pm2.keymetrics.io/ After=network.target [Service] Type=forking User=root LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin Environment=PM2_HOME=/root/.pm2 PIDFile=/root/.pm2/pm2.pid Restart=on-failure ExecStart=/usr/local/lib/node_modules/pm2/bin/pm2 resurrect ExecReload=/usr/local/lib/node_modules/pm2/bin/pm2 reload all ExecStop=/usr/local/lib/node_modules/pm2/bin/pm2 kill [Install] WantedBy=multi-user.target Target path /etc/systemd/system/pm2-root.service Command list [ 'systemctl enable pm2-root' ] [PM2] Writing init configuration in /etc/systemd/system/pm2-root.service [PM2] Making script booting at startup... [PM2] [-] Executing: systemctl enable pm2-root... Created symlink from /etc/systemd/system/multi-user.target.wants/pm2-root.service to /etc/systemd/system/pm2-root.service. [PM2] [v] Command successfully executed. +---------------------------------------+ [PM2] Freeze a process list on reboot via: $ pm2 save [PM2] Remove init script via: $ pm2 unstartup systemd # pm2 save [PM2] Saving current process list... [PM2] Successfully saved in /root/.pm2/dump.pm2 |
デバッグモードで見たい時は、pm2 stopで一度終了して、コンソールから手動でlaravel-echo-server startすればOK!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# pm2 stop echo-chat [PM2] Applying action stopProcessId on app [echo-chat](ids: [ 0 ]) [PM2] [echo-chat](0) ✓ ┌─────┬──────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐ │ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │ ├─────┼──────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤ │ 0 │ echo-chat │ default │ N/A │ fork │ 0 │ 0 │ 2 │ stopped │ 0% │ 0b │ root │ disabled │ └─────┴──────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘ # laravel-echo-server start L A R A V E L E C H O S E R V E R version 1.6.0 ⚠ Starting server in DEV mode... ✔ Running at localhost on port 6001 ✔ Channels are ready. ✔ Listening for redis events... Server ready! |
確かに面倒だ・・・。
これならpusher.comを使え!というlaravelの言い分も分かるわ~。