CentOS7にXAMPPとCakePHP3とNode.js&Socket.ioをセットアップするまでの流れ

CakePHP上にチャットを構築しようとするとき用の備忘録

Socket.ioが通信方法をwebsocketに変更しようとすると、
変更を知らせるパケットのステータスコードが101ではなく
200になってしまうという問題(ポーリングは可能なため一応通信は可能だが、
socket.ioの意味がない)が起こり、その原因が分かりません。

そのため、今回はsocket.io.jsの読み込みはリダイレクトしますが、
そのjsがconnectする先は直接Node.jsにします。スマートじゃないですが
オーバーヘッドは少ないかもしれません。

 
参考にさせていただいたもの

wtbm.necocen.info

qiita.com

得られた知見 

  • ApachePHPにはログファイルがある。

  • 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以外には右クリックのオプションから変更可能。

全部書いているので適度に読み飛ばしてください。

  1. CentOS7をインストールする。今回はGUIにしました。

  2. 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。

  3. もしSELinuxが有効になっていたらApacheが起動しない可能性があるので無効にする←場合に依るんでしょうか?CUIの時は遭遇したがGUIでは遭遇しなかった。

  4. ファイアウォールの80と3000(Node.js用、3000じゃなくても良い)を開ける
    --permanentを忘れずに

  5. まずゲストOS側でApachePHPMyAdminが動いているか確認

  6. ホストOSからphpMyAdminにログインしようとすると新しいXAMPPのセキュリティコンセプトなどとエラーが出るので、/opt/lampp/etc/extra/httpd-xampp.confのrequire localをコメントアウトし、適当なアドレスを許可する。今回はとりあえず動かしたかったので、require all grantedとした。

  7. ゲストOSがNATの場合はポートフォワーディングを設定する

  8. ホストOS側でApachePHPMyAdminが動いているか確認

  9. phpMyAdminCakePHP用のユーザを作る。ホスト名は%だとなぜか動かなかったのでlocalhostとした。

  10. CakePHPのconfig/app.phpのDB設定を編集する。

  11. MariaDBに論理バックアップしておいたSQLをぶちこむ

  12. phpのPATHを通すために~/.bashに以下を追記。
    export PATH=$PATH:/opt/lampp/bin/php←場合による。

  13. composerそのものをインストールする

    CentOS7にComposerをインストールする - Qiita

  14. composer installする。
  1. CakePHPプロジェクトを閲覧すると実行権限がないなどと怒られるので、
    chmod -R 777 /opt/lampp/htdocs/{プロジェクト名}/logs
    chmod -R 777 /opt/lampp/htdocs/{プロジェクト名}/tmp
    のように権限を変更する。777はとりあえず動かしたかったので。

  2. この時点でCakePHPMariaDBとの間に有効なデータベース接続があることを確認する

  3. 【ここからNode.js&Socket.io】

    localhost:3000/nodejs/socket.ioの通信をApacheからNode.jsに移譲する(リバースプロキシ)ために
    Apachehttpd.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は自動で生成されます。

  4. node js,npmをインストールする
    node.jsは最新ではなくLTS版の6.11.0を使用しました。

  5. redisをインストールする

    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

  6. package.jsonがsocket.io,redisなど適切に書かれていることを確認してnpm installする(うまいこと行かなかったらnpm install -g)

  7. use socket.io;を書く

  8. Redisクラスを使えるようにするphp-redisをインストールする

  9. 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する

  10. なんやかんやする。以下実際に書いたコード

今回の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('http://localhost:23000');
        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 /nodejs http://localhost:3000 retry=0 timeout=600 keepalive=On
ProxyPassReverse /nodejs http://localhost:3000

追記

きちんとReadme.mdを読んだらRedisクラスはsocket.io-php-emitterにあった。確かにcomposer.jsonにrequire ptrofimov/tinyredisclientとある。しかし高機能なものを使うならPHP-redisをインストールしろということらしい。