佐藤の屋敷

技術的なことを残せれば幸い

PHPでRedisのPubSubを使ってクライアントに情報を通知したい

久々に書きます。今回のでは完成ではないけど、最低限はクリアしたかな。
わざわざ手間のかかることやってたりしますが許してください。

■目的、したいこと

  • PHPでクライアント別にリアルタイムに情報を通知したい。(RedisのPub/Subを使いたい)
  • ついでにRedisのmaster/slaveも試しでやってみる。

■準備したもの

  • Redis (オープンソースのkey-valueデータストア)
  • php_redis 5.6
  • Node.js
  • Socket.IO

■準備済みのもの

  • vagrant CentOS 6.5 x64
  • XAMPP v3.2.1 (古い) php 5.6

■おおまかな手順

1. vagrantの準備
2. Redisのインストール
3. Redisの設定変更と起動
4. XAMPPのphp 6.5でRedisのextensionを設定する
5. CentOSにもRedisのextensionを設定してみる
6. nodejs準備(nvmの準備)
7. jsでsubscriberを作る
8. 動作させてみる

■おおまかな説明

1. vagrantの準備

今回は3台のサーバを用意しようかなとおもいましてVagrantfileはこんな感じ。コメントのところとか削ってますが。

Vagrant.configure(2) do |config|

  config.vm.box = "puphpet/centos65-x64"
  config.vm.define :web1 do | web1 |
      web1.vm.hostname = "web1"
      web1.vm.network "private_network", ip: "192.168.33.10"
      web1.vm.provider "virtualbox" do |vb|
        vb.memory = "2048"
      end
  end

  config.vm.define :web2 do | web2 |
      web2.vm.hostname = "web2"
      web2.vm.network "private_network", ip: "192.168.33.11"
      web2.vm.provider "virtualbox" do |vb|
        vb.memory = "2048"
      end
  end

  config.vm.define :web3 do | web3 |
      web3.vm.hostname = "web3"
      web3.vm.network "private_network", ip: "192.168.33.12"
      web3.vm.provider "virtualbox" do |vb|
        vb.memory = "2048"
      end
  end

end

ほか、vagrantユーザの他に私の中の慣習としてるdevelopユーザの作成など。。

2. Redisのインストール

以下各サーバでやります。(master/slave)
ちなみにsudoでないとダメな環境の場合は$ sodo に置き換えてください。今回は基本rootでまっしぐらします。

epelの追加 ( centos65 64bit )

# wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm

epelリポジトリのインストール

# rpm -ivh epel-release-6-8.noarch.rpm

epelリポジトリデフォルト無効化するため開く

# vi /etc/yum.repos.d/epel.repo

enable=1をenable=0に変更する。

Redisのインストール

# yum -y install redis --enablerepo=epel

自動起動の設定

# chkconfig redis on

以上でRedisのインストールは完了

3. Redisの設定変更と起動

サービスの起動

# service redis start

バージョンの確認。問題なし。

# redis-server -v
 Redis server version 2.4.10 (00000000:0)

ひとまずiptablesを止めておく。試しで止めているだけです。セキュリティ上いろいろ必要な場合は計画を立てて実行してください。

# /etc/init.d/iptables stop

次にRedisの設定変更をします。今回 192.168.33.10 にあたるweb1はRedisのmasterにして、他のweb2,web3はslaveにします。
設定ファイルは以下となります。

# vi /etc/redis.conf

以下confの中。web1サーバで変更する項目。どこからでも接続を許可する。

#bind 127.0.0.1
bind 0.0.0.0

以下もconfの中。web2,web3で変更する項目。slaveの設定というかmasterサーバはドコドコですよと。

# slaveof <masterip> <masterport>
slaveof 192.168.33.10 6379

以上で設定完了。各サーバでRedisの再起動をする。

# service redis restart

設定を確認する。web1の場合( redis-cliで入り、infoコマンドを実行する )

[root@web1 ~]# redis-cli
redis 127.0.0.1:6379> info
redis_version:2.4.10
:
:
role:master

設定を確認する。web2,web3の場合( redis-cliで入り、infoコマンドを実行する )

[root@web3 ~]# redis-cli
redis 127.0.0.1:6379> info
redis_version:2.4.10
:
:
role:slave
master_host:192.168.33.10
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0

問題なくmasterとslaveで分かれました。これでmasterの更新内容がslaveで参照可能となります。
master_link_statusがdownだと接続失敗しています。ネットワーク周りを確認してください。

また、vm.overcommit_memory=1の対応について今回は省略します。
redisのログや、ネットで検索してみてください。

4. XAMPPのphp 6.5でRedisのextensionを設定する

Windows上というか、XAMPP v3.2.1 php6.5でRedisに接続を試みたいので、dllを設定します。

必要な物は2つ

php_igbinary-1.1.1-5.6-ts-vc11-x86.zip
php_redis-2.2.5-5.6-ts-vc11-x86.zip

php.iniでは以下の様な設定をします。(dllを\xampp\php\extの下に入れてね)

extension=php_igbinary.dll
extension=php_redis.dll

igbinaryはこちら
https://pecl.php.net/package/igbinary/1.1.1/windows

redisはこちら
http://windows.php.net/downloads/pecl/snaps/redis/2.2.5/

あとはwindowsのコマンドプロンプトで以下のように問題無ければOK。
dllが正しくないとバージョンチェックだけで怒られます。もし怒られるようなら、phpバージョンなどを再確認して環境に適したzipをダウンロードして下さい。

C:\Users\satoysan>php -v
PHP 5.6.12 (cli) (built: Aug  6 2015 12:06:20)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
    with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans

Webでどうのこうのしたい場合はXAMPPのApacheを再起動してください。

次に実行テストを行います。
test.php

<?php
$redis = new Redis();
$redis->connect("192.168.33.10",6379);
echo $redis->ping();

実行するとこのような感じに。+PONGが帰ってくればOKです。

C:\Users\satoysan>php  C:\Users\satoysan\IdeaProjects\redisTest\test.php
+PONG

5. CentOSにもRedisのextensionを設定してみる

同様にCentOSにもPHPやらApacheやらをインストールしてPHPからRedisを実行するため、さくっとyumでインストールします。(web1に)

apacheとphpのインストール

# yum -y install httpd php

サービスの起動

# service httpd start

自動起動の設定

# chkconfig httpd on

pecl redisのライブラリインストール
(php redisドキュメント https://github.com/phpredis/phpredis#classes-and-methods)

# yum -y install php-pecl-redis --enablerepo=epel

apache 再起動

# service httpd restart

先ほどのphpプログラムが動作すればOKです。

6. node.js準備(nvmの準備)

それではクライアントを作るためにnode.jsをガツガツインストールします。
これはweb2,web3にインストールを行います。適当な一般ユーザで実行します。
私の環境の場合はdevelopユーザを作っています。

NVM(Node Version Manager)を導入

$ git clone https://github.com/creationix/nvm.git ~/.nvm
$ source ~/.nvm/nvm.sh

node.jsのバージョンチェック

$ nvm ls-remote

現時点での最新バージョンインストール

$ nvm install v5.6.0
$ nvm use v5.6.0
 Now using node v5.6.0 (npm v3.6.0)

デフォルト設定

$ nvm alias default v5.6.0

確認

$ node -v
v5.6.0

npmのアップデート

$ npm update -g npm

npmのグローバルのパッケージをアップデート

$ npm outdated -g
$ npm update -g

実行環境の構築

$ mkdir nodejs_server
$ cd nodejs_server
$ npm init

package.jsonを編集して軽い感じに

{
  "name": "nodejs_server",
  "version": "1.0.0",
  "description": ""
}

package.jsonができているので、使うかも?ライブラリをローカルインストール。

$ npm install --save redis
$ npm install --save socket.io
$ npm install --save socket.io-emitter
$ npm install --save socket.io-redis
{
  "name": "nodejs_server",
  "version": "1.0.0",
  "description": "",
  "dependencies": {
    "redis": "^2.4.2",
    "socket.io": "^1.4.5",
    "socket.io-emitter": "^1.0.0",
    "socket.io-redis": "^1.0.0"
  }
}

7. jsでsubscriberを作る

web2に以下のコードを用意するsubclient.js( web2のチャンネルはtest_1 )

var subscriber = require('redis').createClient(6379, 'localhost');

//個別のチャンネルと全体用のチャンネルを試しで。
subscriber.subscribe(["test_1","all"]);

subscriber.on('message', function(channel, message) {
    console.log('channel: ' + channel + ', message: ' + message);
});

web3に以下のコードを用意するsubclient.js( web3のチャンネルはtest_2 )

var subscriber = require('redis').createClient(6379, 'localhost');

//個別のチャンネルと全体用のチャンネルを試しで。
subscriber.subscribe(["test_2","all"]);

subscriber.on('message', function(channel, message) {
    console.log('channel: ' + channel + ', message: ' + message);
});

web2,web3でjsをそれぞれ実行して待ち状態へ。

$ node subclient.js

8. 動作させてみる

windows上でもweb1でも構わないので、以下のphpを実行してみる。

<?php
$redis = new Redis();
$redis->connect("192.168.33.10",6379);

$redis->publish('test_1', 'test1!! hello, world!');
$redis->publish('test_2', 'test2!! hello, world!');

$redis->publish('all', 'test users! hello, world!');

上記を実行するとweb2,web3で起動していたsubclient.jsの元にそれぞれメッセージが無事届いて幸せに。

web2

channel: test_1, message: test1!! hello, world!
channel: all, message: test users! hello, world!

web3

channel: test_2, message: test2!! hello, world!
channel: all, message: test users! hello, world!

一応届いている。
使ったり使ってなかったりいろいろ中途半端だけど。。。
次回はもっとクライアント!ッて感じへ続く。図を次は書きます。