React +

この投稿は2年半前の記事です。 情報が古くなっている可能性があるので、その点ご了承ください。
2014 年 6 月 18 日 1,012日前)
5,983文字 (読了時間14分)

SPONSORED LINK

最近PythonのTornadoというのを使っていて、はじめてノンブロッキングWebサーバをまともに触ったのですが、いままで自分がやってきたWebプログラミングとは結構違っていて、面白く感じました。

特に1つのアクセスが1つのクラスに対応するというのが面白かったです。これ説明したら「は?」って言われたんですけどね。

で、僕が管理しているサイトはほとんどWordPressなので、残念ながらいわゆる普通のPHPアプリケーションです。最近はBackbone.JSとかAngularJSでページ遷移なしにガンガンUIが変わるWebアプリケーションが流行りですが、そういうサイトは凄まじい数のAjaxリクエストが飛んでくるので、WordPressだとサーバがパンクしてしまいます。

特にWordPressのAjax APIは毎回WordPressの起動処理を行うので、大変遅いです。

じゃあNode.JSなりTornadoなりでサイトを作り直すのかというと、それもそれでメンドクサイですね。それに、データベース周りとか、せっかくWordPressの資産があるのだから、それを利用したいものです。

で、そういうノンブロッキングWebサーバになるPHPないかなーと探してみたところ、Reactというのがありました。

ReactPHP
ReactPHP

Reactを試してみる

このReactはComposerとかでサクッとインストールできます。Bring High Performance Into Your PHP App (with ReactPHP)っていう記事を見ながらやったところ、すぐできました。今回はこんな構成でインストールしてみます。

  1. ローカルのMacにはWordPressをインストール済み。ドメインは takahashifumiki.info で ルートディレクトリは /opt/local/www/fumiki
  2. WebサーバはNginx + PHP-FPM
  3. Reactはルートディレクトリ直下のreactにインストール

で、とりあえず動くとこまで持って行きます。

# とりあえずディレクトリに移動。
cd /opt/local/www/fumiki/react
# composerでインストール
composer require 'react/react=*’
# サーバ起動スクリプトを作る
touch server.php

このserver.phpがメインになります。これを編集。上の参考サイトからまんまコピーして、コメントを追記してます。

<?php
// オートローダーを読み込み

require 'vendor/autoload.php’;
// アクセスごとにインクリメントしてく
$i = 0;
// メイン関数。アクセスごとにこれが起動。
$app = function ($request, $response) use (&$i) {
    $response->writeHead(200, array('Content-Type' => 'text/plain'));
    $response->end("Hello World $i\n");
    $i++;
};

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket, $loop);

$http->on('request', $app);
echo "Server running at http://127.0.0.1:1337\n";

$socket->listen(1337);
$loop->run();

で、再び黒い画面に戻り、コマンドラインからサーバを起動します。

php server.php
//-> Server running at http://127.0.0.1:1337

これでサーバが起動したので、ブラウザからhttp://127.0.0.1:1337にアクセスしてみます。すると、”Hello World 1” という感じで表示され、リロードするごとにインクリメントしていきます。

サーバの終了はCtrl-Cです。

WordPressとReactを同居させる

さて、これだけだとなんのこっちゃですが、WordPressと連携してみます。まず、/reactでアクセスしたときはReactのサーバが反応し、それ以外だと普通のWordPressとして動くという設定にしましょう。たぶんですが、Nginxじゃないとダメだと思います。

で、Nginxの設定ファイルをこんな感じに設定します。httpブロックにReactサーバを追加、serverブロック(WordPressの設定がゴチャゴチャ書いてある部分)にプロキシへ渡す処理を書きます。

http{
    # プロキシを定義
    upstream react {
        server 127.0.0.1:1337;
    }
    # WordPressの定義に追加
    server{
        # serverブロックにゴチャゴチャ書いてあるはず
        location ~ ^/react {
            proxy_pass_header Server;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Scheme $scheme;
            proxy_pass http://react;
        }
    }
}

これでReactサーバを起動したままNginxを再起動すると、/reactでだけさっき作った Hellow World が出て、それ以外はWordPressとして普通に動くという仕組みになるはずです。

ここまででも「だからなんなの?」と感じが否めませんが、気にせず続けます。

WordPressのAjax APIとReactをパフォーマンス比較

さて、やっと本題です。WordPressには元々Ajax APIが存在しますが、ここで次のようなパフォーマンステストを行ってみます。

  1. データベースからブログのタイトルを表示するという関数を作成
  2. WordPressのAjax APIとReactで同じ関数を実行してみて、速度を比較

では、まず関数を定義しましょう。これはテーマのfunctions.php内に書いてしまいます。

get_optionを使わないのは、僕がMemcachedを使ってしまっているからですね。あまり深い意味はないです。

/*
 * WordPressのデータベースに保存されたブログ名を返す
 * @return string
 */
function fumiki_greeting(){
    global $wpdb;
    // 名前を毎回取得する
    $name = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'blogname'");
    // クエリを空にして、次回も取得されるようにする
    $wpdb->last_query = '';
    // HTMLを生成
    $html = <<<EOS
<html>
<head>
<title>Performance Test</title>
</head>
<body>
<h1>Hello, this site is %s</h1>
</body>
</html>
EOS;
    return sprintf($html, esc_html($name));
}

では、Ajax経由でこの関数を実行してみましょう。同じくfunctions.php内に書きます。

add_action('wp_ajax_nopriv_greeting', function(){
    echo fumiki_greeting();
    exit;
});

これで takahashifumiki.info/wp-admin/admin-ajax.php?action=greeting にアクセスすると、HTMLが表示されます。

続いて、Reactのserver.phpも変えます。ポイントとしてはwp-load.phpを読み込むことですかね。

<?php
// wp-load.phpを読み込む。
require '../wp-load.php';
require 'vendor/autoload.php';

$app = function($request, $response) use ($wpdb) {
    // Entry Point
    $response->writeHead(200, array('Content-Type' => 'text/html'));
    $response->end(fumiki_greeting());
};

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket, $loop);
$http->on('request', $app);
echo 'Server running at http://127.0.0.1:1337'.PHP_EOL;
$socket->listen(1337);
$loop->run();

おそらく、wp-load.phpを読み込むことでエラーがぐわーっと出るかと思いますが、これはまあしょうがないですね。なんかのプラグインが if ($_SERVER[‘REQUEST_METHOD’] == ‘POST’) とか書いてるんですよ。これはPHPerのカルマみたいなもんです。

Reactを再起動したら、takahashifumiki.info/react にアクセスしてみます。すると、先ほどとまったく同じ画面が表示されます。

Reactでも同じ画面
Reactでも同じ画面

では、この二つのURLに対してApache Benchをかましましょう。コマンドは ab -n 1000 -c 100 TARGET_URL ですね。

WP Ajax React 意味
要した時間 24.713秒 2.329秒 短いほどいい
秒間リクエスト 40.46 429.3 多いほどいい
1リクエストの時間 24.713 2.329 小さいほどいい

なんとなくですが、Reactの方が10倍ぐらい早い結果ですね。

まとめ

WordPressは起動するたびに毎回データベースへ接続して、どのプラグイン・テーマ使うかとか、俺のURLなんやねんとか、そういうことを毎度行います。したがって、Ajaxのエンドポイントもなんとなくもっさりしています。Reactにすると爆速になること請け合い。

もちろん、Reactを導入するとWordPressのAjax APIと似たようなものを自分で実装しなければいけないのですが、現時点のAjax APIはあんまり便利ではないので別に気にならないと思われます。

ここら辺実装すると使い物になるんじゃないですかね。

  • ルーティングで、Reactサーバへのリクエストを適切なクラスに振り分ける処理。CakePHPとかのコントローラーを読み込んで行くようなヤツ。
  • データベース接続が起動しっぱなしなので、適宜リフレッシュするなり、再接続するなりの処理。
  • MemcachedなどのKVSを適切に利用する(Object Cache API経由で)
  • WordPressの管理画面からReactサーバの健全性を確認できる
  • Supervisordとかでデーモン起動する。で、Nginxをロードバランサー的に使えばOK。

注意点としては以下の通り。

  • WordPressのユーザー周り(get_current_userとか)は1プロセスあたり1ユーザーを想定しているので、Reactだとうまく動かないかも。認証系はWordPress側で行ってからReactに向けるか、Cookie読み取りの処理をReactにやらせるのがスマートか。
  • ノンブロッキングサーバで「起動時点からMySQLにつなぎっぱなし」というのが有りなのかどうかはわかりません。

Reactと同じ方法でRatchetというWebSocketサーバも実装できるので、WordPressのユーザー情報と組み合せたチャットルームやメッセージシステムなんかも作れますね。実は僕の本命はこっちだったりするんですけどね。終わり。

ステートフルJavaScript ―MVCアーキテクチャに基づくWebアプリケーションの状態管理

ステートフルJavaScript ―MVCアーキテクチャに基づくWebアプリケーションの状態管理 [書籍]

著者Alex MacCaw

クリエーター牧野 聡

出版社オライリージャパン

出版日2012 年 6 月 9 日

商品カテゴリー大型本

ページ数304

ISBN487311554X

Supported by amazon Product Advertising API

 

フォローしてください

ここで会ったのもなにかの縁。
高橋文樹.comの最新情報を見逃さないためにもフォローをお願いします。
めったに送らないメルマガもあります。

SPONSORED LINK

この記事について

この記事はが2014 年 6 月 18 日にプログラミングの記事として公開しました。

高橋先生の電子書籍

高橋先生の電子書籍

Amazonで電子書籍も買えます。

好きな言葉

イギリス人は冷静沈着で、人生の出来事を――たとえどれほど悲劇的なことであれ――ユーモアとともに受け止めるやり方を心得てるとよく言われます。かなり当たっています。それがイギリス人の本当に馬鹿なところなんです。ユーモアは救いにならない。結局のところ、ユーモアなどほとんど何の役にも立たないものです。何年間か、あるいはもっと長いあいだ人生上の出来事をユーモアとともに受け止め、場合によってはほとんど最後までユーモアに富んだ態度を貫くこともできるでしょう。とはいえ最後には、人生は人の心を打ち砕かずにはいない。

— ミシェル・ウエルベック

高橋先生の処女作

『途中下車』高橋文樹

2001年幻冬舎NET学生文学大賞受賞作です。

Web制作やります

Web制作やります

Web制作のご依頼は株式会社破滅派へ

不定期メルマガ

高橋文樹.comでは、不定期でニュースレターを配信しています。滅多に送らないので是非購読してください。

高橋文樹.comではプライバシーポリシーに準じて登録情報を取り扱います。