Apache::Session モジュール v1.52

by Hippo2000(2000/8/8, 2001/2/7,2001/9/13)

(2001/9/13) まこと@FESさんからの指摘を受けて、誤字を訂正しました。
(2001/2/7 ) まこと@FESさんからの指摘を受けて2ヶ所、誤字を訂正しました。

日本語チョウ訳シリーズ Apache::Sessionモジュールなのです。

Apacheと名がついていますが、ドキュメントにもあるとおりApacheやmod_perlに依存しているわけではありません。オブジェクトの永続化とID生成を組み合わせることでHTTPで(擬似的な)ステートフルな通信を可能にしています。CGI::Persistentとほぼ同じ考えではないかと感じています。

クッキーを使わなくてもいいという点が、結構お気に入り(といってまだ試していないんですが)。でもユーザが勝手に抜けていってしまった時などで、必要となりそうなセッションのタイムアウトについては考えられていないようなので、その当たりはちょっと気になすが...

原本の著作者はJeffrey Baker氏です。Jeffrey Baker氏にはメールで了解を頂きました。

内容等が間違っていたら修正します。ご連絡ください。

なおApache::Sessionには以下のサブクラスがあります。こちらもご覧下さい。

クラス名 説 明
Apache::Session::DB_File Apache::Sessionの実装の1つ
Apache::Session::File Apache::Sessionの実装の1つ
Apache::Session::Flex 実行時にすべてを指定する
Apache::Session::MySQL Apache::Sessionの実装の1つ
Apache::Session::Oracle Apache::Sessionの実装の1つ
Apache::Session::Postgres Apache::Sessionの実装の1つ
Apache::Session::Sybase Apache::Sessionの実装の1つ
Apache::Session::Generate::MD5 MD5を使ってランダムなオブジェクトIDを作成する
Apache::Session::Lock::File flockを使って相互排他を提供する
Apache::Session::Lock::MySQL MySQLを使ってにより相互排他を提供する
Apache::Session::Lock::Null 実際には相互排他を提供しない
Apache::Session::Lock::Semaphore セマフォを使って相互排他を提供する
Apache::Session::Serialize::Base64 StorableとMIME::Base64を使って永続データを保存できるようにする
Apache::Session::Serialize::Storable Storableを使って永続データを保存できるようにする
Apache::Session::Serialize::Sybase 永続データを格納できるようにするためStorableを使い、Sybase互換のイメージ・フィールドに入れるためにunpack/packする
Apache::Session::Serialize::UUEncode Storableとpack()を使って永続データを格納できるようにする
Apache::Session::Store::DB_File DB_Fileに永続オブジェクトを格納する
Apache::Session::Store::File ファイルシステムに永続オブジェクトを格納する
Apache::Session::Store::MySQL MySQLデータベースに永続オブジェクトを格納する
Apache::Session::Store::Oracle Oracleデータベースに永続オブジェクトを格納する
Apache::Session::Store::Postgres Postgresデータベースに永続オブジェクトを格納する
Apache::Session::Store::Sybase Sybaseデータベースに永続オブジェクトを格納する


名前

Apache::Session - セッションデータのための永続フレームワーク


概要

  use Apache::Session::MySQL;
  
  my %session;
  
  #最初に訪れた人のために新しいセッションを作る
  tie %session, 'Apache::Session::MySQL';

  #そこに何か値を入れる
  $session{visa_number} = "1234 5678 9876 5432";
  
  #後で使うようにセッションIDを取得する
  my $id = $session{_session_id};
  
  #...時間が経過...
  
  #ある他のリクエストでセッションデータを元に取得する
  my %session;
  tie %session, 'Apache::Session::MySQL', $id;
  
  &validate($session{visa_number});
  
  #オブジェクト・ストアから永遠にセッションを削除する
  tied(%session)->delete;
  



説明

Apache::Sessionはhttpdリクエストの間でセッション・データを追いかけるのに特に便利な永続化フレームワークです。Apache::SessionはApacheとmod_perlと一緒に機能するように設計されていますが、CGIや他のwebサーバの元でも機能するはずですし、Webサーバからまったくはなれたところでも機能します。

Apache::Sessionは5つのコンポーネントで構成されます:インターフェース、オブジェクト・ストア、ロック・マネージャ、IDジェネレータ、そしてシリアライザです。インターフェースはSession.pmで定義されています。つまり簡単にサブクラスにすることができます。オブジェクト・ストアはファイルシステム、バークレーDB、MySQL DB、Oracle DBまたはPostgres DBにすることができます。ロックはロック・ファイル、セマフォ、またはMySQLとPostgresのロック機能によって行われます。シリアル化はStorableを通して行われ、オプションでMIMEやpack()を介してASCII化されます。ID番号はMD5を介して作成されます。読者は自分の要求にあうようにこれらの機能を拡張するように奨励されます。

Apache::Sessionから派生したクラスは3つのコンポーネントを一緒に結びつけるために使われます。派生したクラスはApache::Sessionからインターフェースを継承し、利用するストアとロッカーを特定します。例えばApache::Session::MySQLはMySQL格納クラスとMySQLロッキングクラスを使います。独自のオブジェクト・ストアやロッカー・クラスを組込むことが出来ます。


インターフェース

Apache::Sessionへのインターフェースはとても簡単です:ハッシュを好きなクラスに結び付け(tie)、ハッシュを通常のように使ってください。コンストラクタは2つのオプションの引数を取ります。最初の引数は好きなセッションID番号です。undefであれば新しいセッションです。2番目の引数はオブジェクト・ストアとロッカー・クラスに渡されるオプションのハッシュです。

セッションの結びつけ

DBIを使って新しいセッションを取得してください:

 tie %session, 'Apache::Session::MySQL', undef,
    { DataSource => 'dbi:mysql:sessions' };
    

データベースから古いセッションを元に戻してください:

 tie %session, 'Apache::Session::MySQL', $session_id,
    { DataSource => 'dbi:mysql:sessions' };

データのセッションへの格納し、取り出し

あぁ、どれくらいもっと簡単になるでしょう?

 $session{first_name} = "Chuck";
 $session{an_array_ref} = [ $one, $two, $three ];
 $session{an_object} = new Some::Class;

セッションIDの読み込み

セッションIDはセッションオブジェクトでの唯一のマジックエントリです。しかし"_"で始まるものはすべて将来の利用のために予約されていると考えられています。

 my $id = $session{_session_id};

記憶からセッションを永遠に削除する

 tied(%session)->delete;


動き

Apache::Session は、あなたが期待していると作者が信じている方法で動きます。新しいセッションを作ると、Sessionはそのセッションをデータ・ストアに格納するか、できなければdie()を呼びます。

セッション・オブジェクトに占有ロックも取得します。既存のセッションを取り出すと、Sessionは即座に格納領域からオブジェクトを元に戻すか、エラーの場合はdie()を呼びます。Sessionはセッションに占有ではないロックも取得します。

セッション・ハッシュにデータを入れると、Sessionは後から使うように格納します。セッション・ハッシュをuntie()した、またはスコープからはずれたとき、Sessionは何か変更されていないかをチェックします。そうであれば、Sessionは占有ロックを取得し、データ・ストアにそのセッションを書きこみます。そしてそれまでに取得したすべてのロックを解放します。

Apache::Sessionは何か変更されていないか影でチェックするだけだということに注意してください。最上位のtiedハッシュ何も変更がなければデータは裏のストアで更新されません。更新されることを確実にするには、セッション・ハッシュにタイムスタンプを入れることが勧められます。

セッション・オブジェクトにdelete()を呼ぶと、可能であればそのオブジェクトはオブジェクト・ストアから即座に削除されます。

Sessionがエラーにであうと、それはdie()を呼びます。これらのエラーをトラップするよう、たぶんセッションのロジックをevalブロックに包みたいでしょう。


ロックとトランザクション

デフォルトでは、ほとんどのApache::Session実装はデータがおかしくなるのを防ぐためだけにロックします。ロック機能はリレーショナル・データベースから得られるような、トランザクション的な一貫性を提供しません。、トランザクション的な一貫性が欲しいのであれば、セッション・ハッシュをタイ(tie)するときに、Transactionをtrueの値で提供してください。例えば:

 tie %s, 'Apache::Session::File', $id {
    Directory     => '/tmp/sessions',
    LockDirectory => '/var/lock/sessions',
    Transaction   => 1
 };

Transaction引数はMySQLとPostgres実装では実際には何も効果がないことに注意してください。MySQL実装は単に占有ロックをサポートしているだけですし、Postgres実装はそのデータベースのトランザクション機能を使っています。


実装

Apache::Sessionを実装する方法は、何を実現しようとしているのかに依存します。どの状況ではどのクラスを使うかのヒントがいくつか、ここにあります。


戦略

Apache::Session は主にhttpdリクエストの間でユーザ・セッションを追いかけるように設計されています。しかしそれはデータの永続化が欲しいすべての状況で使うことも出来ます。例えばhttpdプロセスの間でグローバルなデータを共有するために使うことが出来ます。以下の例は短いmod_perlプログラムで、セッションの取り扱いの基本をいくつか示しています:

Apacheプロセス間でデータを共有する

Apacheプロセスの間でデータを共有するとき、先にセッションID番号を決め、Apacheを開始する前にオブジェクト・ストアにそのID番号を持ったオブジェクトがあることを確認する必要があります。それをどのように実現するかはあなたの仕事です。私はセッションID "1"を使います。データベース・アクセス情報を取り出すためにApache::Sessionを使う短いプログラムを以下に示します:

 use Apache;
 use Apache::Session::File;
 use DBI;

 use strict;
  
 my %global_data;
 
 eval {
     tie %global_data, 'Apache::Session::File', 1,
        {Directory => '/tmp/sessiondata'};
 };
 if ($@) {
    die "Global data is not accessible: $@";
 }

 my $dbh = DBI->connect($global_data{datasource}, 
    $global_data{username}, $global_data{password}) || die $DBI::errstr;

 undef %global_data;
 
 #プログラムが続く...
 

この例で示されたように、それを使い終わったらすぐにセッションをundefまたはuntieしなければいけません。これはプロセスに関連付けられたすべてのロックを解放します。

ユーザをクッキーで追いかける

ユーザIDを追いかけるのにクッキーを使うか、パス情報を使うかの選択は、Apacheユーザの間ではむしろ宗教的な話題です。この例ではクッキーを使います。パス情報システムの実装は読者への練習として残っています:

 use Apache::Session::MySQL;
 use Apache;

 use strict;

 #もし古いセッションであればクッキーを読み込む

 my $r = Apache->request;
 my $cookie = $r->header_in('Cookie');
 $cookie =~ s/SESSION_ID=(\w*)/$1/;

 #ブラウザから取得したクッキー、もしくはクッキーを取得しなければ
 #新しいセッションを元にセッション・オブジェクトを作成する。

 my %session;
 tie %session, 'Apache::Session::MySQL', $cookie, {
      DataSource => 'dbi:mysql:sessions', #these arguments are
      UserName   => 'mySQL_user',         #required when using
      Password   => 'password',           #MySQL.pm
      LockDataSource => 'dbi:mysql:sessions',
      LockUserName   => 'mySQL_user',
      LockPassword   => 'password'
 };

 #新しいセッションかもしれません、そこでクッキーを返させましょう

 my $session_cookie = "SESSION_ID=$session{_session_id};";
 $r->header_out("Set-Cookie" => $session_cookie);

 #プログラムは続く...


参考資料

(原文のまま)

Apache::Session::MySQL, Apache::Session::Postgres, Apache::Session::File, Apache::Session::DB_File

The O Reilly book "Apache Modules in Perl and C", by Doug MacEachern and Lincoln Stein, has a chapter on keeping state.


作者

(原文のまま)

Jeffrey Baker <jwbaker@acm.org> is the author of Apache::Session.

Chris Winters <cwinters@intes.net> contributed the Sybase code.

Michael Schout <mschout@gkg.net> fixed a commit policy bug in 1.51.

Andreas J. Koenig <andreas.koenig@anima.de> contributed valuable CPAN advice and also Apache::Session::Tree and Apache::Session::Counted.

Gerald Richter <richter@ecos.de> had the idea for a tied hash interface and provided the initial code for it. He also uses Apache::Session in his Embperl module and is the author of Apache::Session::Embperl

Jochen Wiedmann <joe@ipsoft.de> contributed patches for bugs and improved performance.

Steve Shreeve <shreeve@uci.edu> squashed a bug in 0.99.0 whereby a cleared hash or deleted key failed to set the modified bit.

Peter Kaas <Peter.Kaas@lunatech.com> sent quite a bit of feedback with ideas for interface improvements.

Randy Harmon <rjharmon@uptimecomputers.com> contributed the original storage-independent object interface with input from:

  Bavo De Ridder <bavo@ace.ulyssis.student.kuleuven.ac.be>
  Jules Bean <jmlb2@hermes.cam.ac.uk>
  Lincoln Stein <lstein@cshl.org>

ホーム Perlの小技

ご意見、ご質問はこちらの掲示板で受け付けています。
またメールは河馬屋(Nifty)にお願いします。