CakePHP上にチャットを構築しようとするとき用の備忘録
Socket.ioが通信方法をwebsocketに変更しようとすると、
変更を知らせるパケットのステータスコードが101ではなく
200になってしまうという問題(ポーリングは可能なため一応通信は可能だが、
socket.ioの意味がない)が起こり、その原因が分かりません。
そのため、今回はsocket.io.jsの読み込みはリダイレクトしますが、
そのjsがconnectする先は直接Node.jsにします。スマートじゃないですが
オーバーヘッドは少ないかもしれません。
参考にさせていただいたもの
得られた知見
- ApacheとPHPにはログファイルがある。
- php -mやphpinfo();で読み込まれているモジュールを確認できる。
今回はここでredisとigbinaryが読み込まれていることを確認した。 - RedisとNode.jsにはデバッグモードがある。
- elephant.ioはクライアントとして動作する(その為node.jsでフィルタ可)
php-emitterはサーバから送信(emit)する(その為node.jsではフィルタ出来ない、イベントもない)
今回はphp-emitterを使用し、こちらからのみ配信する仕様だったので、socket.ioにはほぼ何も書いていません。 - class not foundに悩まされているときは以下を試す。
チーム開発で、それぞれ実行環境が違う時に存在しないクラスを使用しない為にも使える。 - //ロード済みのextension一覧を表示する
debug(get_loaded_extensions()); - //include / require済みのファイル一覧を表示する、
今回これでcomposerのファイルが読み込めてないことに気づいた
debug(get_included_files()); - //宣言済みのクラス一覧を表示する
debug(get_declared_classes()); - yumでインストールしたパッケージがどこにインストールされたか確認する
rpm -ql パッケージ名 - yumでインストール可能なorインストールした(リポジトリ名の前に@がつく)
パッケージ一覧を表示する
yum list (お好みでgrep) - yumでインストールしたパッケージ一覧を表示する
yum list installed - runやshをダブルクリックで実行するには実行権限を付加することが必要。
chmod以外には右クリックのオプションから変更可能。
全部書いているので適度に読み飛ばしてください。
- CentOS7をインストールする。今回はGUIにしました。
- XAMPP for Linuxをダウンロードしてインストールする。
もし、PHPのバージョンを7.1にした場合は unable to initialize module Module compiled with module API=20151012 PHP compiled with module API=20160303と出る。この場合動作するPHPのバージョンは7.0。 - もしSELinuxが有効になっていたらApacheが起動しない可能性があるので無効にする←場合に依るんでしょうか?CUIの時は遭遇したがGUIでは遭遇しなかった。
- ファイアウォールの80と3000(Node.js用、3000じゃなくても良い)を開ける
--permanentを忘れずに - まずゲストOS側でApache、PHPMyAdminが動いているか確認
- ホストOSからphpMyAdminにログインしようとすると新しいXAMPPのセキュリティコンセプトなどとエラーが出るので、/opt/lampp/etc/extra/httpd-xampp.confのrequire localをコメントアウトし、適当なアドレスを許可する。今回はとりあえず動かしたかったので、require all grantedとした。
- ゲストOSがNATの場合はポートフォワーディングを設定する
- ホストOS側でApache、PHPMyAdminが動いているか確認
- phpMyAdminにCakePHP用のユーザを作る。ホスト名は%だとなぜか動かなかったのでlocalhostとした。
- CakePHPのconfig/app.phpのDB設定を編集する。
- MariaDBに論理バックアップしておいたSQLをぶちこむ
- phpのPATHを通すために~/.bashに以下を追記。
export PATH=$PATH:/opt/lampp/bin/php←場合による。 - composerそのものをインストールする
- composer installする。
- CakePHPプロジェクトを閲覧すると実行権限がないなどと怒られるので、
chmod -R 777 /opt/lampp/htdocs/{プロジェクト名}/logs
chmod -R 777 /opt/lampp/htdocs/{プロジェクト名}/tmp
のように権限を変更する。777はとりあえず動かしたかったので。 - この時点でCakePHPとMariaDBとの間に有効なデータベース接続があることを確認する
-
【ここからNode.js&Socket.io】
localhost:3000/nodejs/socket.ioの通信をApacheからNode.jsに移譲する(リバースプロキシ)ために
Apacheのhttpd.confをいじる。今回は、socket.io.jsの読み込みのみApacheを経由させ、
socket.io自体の通信は直接Nodeに渡します。なのでLoadModule ws-tunnelなどは記述せず、ProxyRequests Off
ProxyPass /nodejs http://localhost:3000 retry=0 timeout=600 keepalive=On
ProxyPassReverse /nodejs http://localhost:3000
とした。
余談ですが、socket.ioが起動した時にsocket.io.jsは自動で生成されます。 - node js,npmをインストールする
node.jsは最新ではなくLTS版の6.11.0を使用しました。 - redisをインストールする
makeとは、Makefileファイルに記述されているコマンドを実行し、コンパイルするコマンド
make installとは、コンパイルした結果を適切な場所にコピーしてコマンドラインから使えるようにしたりするコマンド
socket.io-php-emitter で PHP から emit する方法 - Qiita
wget http://download.redis.io/releases/redis-3.2.3.tar.gz
tar xzf redis-3.2.3.tar.gz
cd redis-3.2.3
make
make install
utils/install_server.sh - package.jsonがsocket.io,redisなど適切に書かれていることを確認してnpm installする(うまいこと行かなかったらnpm install -gする)
- use socket.io;を書く
- Redisクラスを使えるようにするphp-redisをインストールする
- extension=なんたらかんたら/redis.soとphp.iniに書くと、Unable to load dynamic library '/usr/lib64/php/modules/redis.so' - /usr/lib64/php/modules/redis.so: undefined symbol: igbinary_serialize in Unknown on line 0とエラーが出るのでyum install php70-php-igbinaryする
- なんやかんやする。以下実際に書いたコード
今回のVIrtualBoxの設定は、NAT接続でポートフォワーディングを23000→3000, 27780→80としています。
Apacheは80, Node.jsは3000, Redisは6379 です。
Node.js
ただCakePHPの一部に表示したかっただけなので、expressは入れていません。
上に書いた通り、PHP-emitterはサーバとして動作するのでconnectionイベントには接続されたことを通知するemitのみしています。
var app = require('http').createServer(handler); var io = require('socket.io')(app); app.listen(3000); var redis = require('socket.io-redis'); var adapter = io.adapter(redis({ host: '127.0.0.1', port: 6379 })); function handler (req, res) { res.writeHead(200); res.end(); } io.on('connection', function (socket) { socket.emit("messageFromPHP",""); });
CakePHP3 Controller
最初の方でPHP7.1を使用していたため、useが使用できずrequire_onceでphpファイルを一つ一つ読み込んでいましたが、PHP7.0に替えたところ特に読み込まずとも認識しました。6379はredisの標準ポートです。
TomController.php
$redis = new \Redis; $redis->connect('127.0.0.1', '6379'); $emitter = new \SocketIO\Emitter($redis); $emitter->emit('messageFromPHP', $num);
composer.json
上と同じく、PHP7.1を使用していたためにautoloadにemitterを入れたりしましたが、PHP7.0に入れ替えたところ、結果的にrequireを書き換えるのみでした。
"ashiina/socket.io-emitter": "0.8.0"
package.json
ただCakePHPの一部に表示したかっただけなので、expressは入れていません。
nodemonはコードが変更されたとき即時に変更を反映するものです。
{
"name": "sample-server",
"main": "off.js",
"dependencies": {
"socket.io": "^1.4.8",
"socket.io-redis": "^1.0.0"
},
"devDependencies": {
"nodemon": "^1.9.1"
}
}
CakePHP3 Template
Apache経由でNode.jsからsocket.io.jsを読み込んだあと、直接Node.jsにconnectしています。Apache経由で読み込む利点は、ApacheとNode.jsが異なるオリジン(ポート番号が違うため)であるので、Node.js側にクロスオリジンの設定(res.header("Access-Control-Allow-Origin", "*"など)がいらないことです。
ここのlocalhostは必要に応じて$_SERVER['SERVER_ADDR']とでもしてください。
bobby.ctp
<script src="/nodejs/socket.io/socket.io.js"></script> <script type="text/javascript"> var socket = io(' ); socket.on('messageFromPHP', function (data) { console.log(data); $('#activity-text').text(data); }); </script>
httpd.conf
websocketの通信を媒介する場合はws-tunnelのモジュールをロードなどするらしいです。私は出来ませんでした。
retry=0はリダイレクト先(Node.js)に接続出来ない状況でその情報をキャッシュさせないものです。つまり0ならば接続のたびにNode.jsに接続できることを確認します。実用的には3秒程度なんでしょうか?
ProxyRequests Off ProxyPass /nodejsretry=0 timeout=600 keepalive=On ProxyPassReverse /nodejs
追記
きちんとReadme.mdを読んだらRedisクラスはsocket.io-php-emitterにあった。確かにcomposer.jsonにrequire ptrofimov/tinyredisclientとある。しかし高機能なものを使うならPHP-redisをインストールしろということらしい。