[PHP-users 30551] セッションをNFS経由で共有してみました。

Shu Sawada luna @ lunanet.gr.jp
2006年 10月 18日 (水) 22:32:05 JST


さわだと申します。

複数のwebサーバ上でPHPアプリが走っている場合、どのようにセッションを共有するかが
大きな問題となりがちかと思います。
その対処として、sharedance や memcached、データベースによるセッション共有などが
よく行われる手段(?)と思います。

実際私もデータベースによるセッション共有( session_pgsql )を使用していたのですが、
そもそも負荷を下げる目的でwebサーバを増やしているのに、ページへのアクセスの度に
セッション参照のためデータベースへアクセスに行くのは如何なものか、というジレンマを
感じていました。

そこで sharedance を使用できないか検討していたのですが、どうもこれはセッションファイルを
flock()しないようで、何となく気持ち悪い感じがします。
これならNFSでそのまま共有するのと変わらないのでは?という疑問に当たりました。
(本当のところは問題ないのかもしれませんが)

そんなこんなでいろいろ調べていたのですが(経過ばっさり略)、結論として簡単な1行パッチ
を当てることでNFS上にセッションファイルを配置した場合でも排他制御が問題なく行うことが
できるような気がしています。(flock()をphp_flock()に変更するだけです)

そこで疑問ですが、
- みなさんのところで動きますか? (興味ある方、追試していただけませんか?)
- mod_files.c で ext/standard/flock_compat.h のincludeまでしているのに、なぜ生のflock()が
 呼ばれているのでしょうか?何か罠があるのでしょうか?

どうも、簡単すぎて納得できないというか、何と言うか。
もしこんなパッチでNFS経由でのセッション共有が可能になるとすると、非常にシンプルな
方法でデータを持ちまわれてオイシイな、と感じておりますです。

以下にパッチと検証コードを示します。


php-4.4.4/ext/session/mod_files.c
----------------

*** mod_files.c.org     2006-10-19 02:45:41.000000000 +0900
--- mod_files.c 2006-10-19 02:46:54.000000000 +0900
***************
*** 159,165 ****
                data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, 0600);

                if (data->fd != -1) {
!                       flock(data->fd, LOCK_EX);

  #ifdef F_SETFD
  #ifndef FD_CLOEXEC
--- 159,165 ----
                data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, 0600);

                if (data->fd != -1) {
!                       php_flock(data->fd, LOCK_EX);

  #ifdef F_SETFD
  #ifndef FD_CLOEXEC

----------------

検証環境:
NFSサーバ: CentOS3.8 [/etc/exports option= (rw,root_squash,sync)]
Webサーバ: Vine 3.2 / php-4.4.4 [ ./configure --with-apxs=/usr/sbin/apxs ] *2台

検証コード:
サーバ1でのスクリプト( ?sid=.... として適当なセッションIDで呼ぶ)
----------------
<?php
        session_id($_GET["sid"]);
        session_start();

        echo "sessid: " . session_id() . "<BR>\n";
        if( !isset($_SESSION["a"]) )
                $_SESSION["a"] = 0;
        $_SESSION["a"]++;

        print "a:" . $_SESSION["a"];

        sleep(10);
?>
----------------

サーバ2でのスクリプト(サーバ1と同じsidを付けて呼ぶ)
----------------
<?php
        session_id($_GET["sid"]);
        session_start();

        echo "sessid: " . session_id() . "<BR>\n";
        if( !isset($_SESSION["a"]) )
                $_SESSION["a"] = 0;
        $_SESSION["a"]++;

        print "a:" . $_SESSION["a"];
?>
----------------

やっていることは、サーバ1でセッションファイルを掴んでる間にサーバ2から呼ぶと
どうなるか、です。

パッチ適用前はサーバ1でsleep()しているときでもサーバ2のスクリプトを実行できて
しまい、どちらもa:の値が同じ値を返してきます。
このことから、flock()が効いていないことが判ります。
パッチ適用後は、サーバ1の実行が終わるまでサーバ2は待ち状態となり、サーバ1
のa:の値+1の値がサーバ2のa:の値となります。
このことから、うまく行っているような気がします。

どんなもんでしょうか?

#php-usersに投げるネタじゃなかったらすみません。

-- 
Shu Sawada
http://luna.lunanet.gr.jp/
http://www.cycleof5th.com/


PHP-users メーリングリストの案内