実際の実装方法
本稿の実装例では、アクセスするWebサーバーを固定化するのではなく、セッション情報の保存先を固定化してHTTPセッションの永続性を確保することにします(リスト3)。
リスト3:PHPのPostgreSQL用セッションセーブハンドラの実装例
// pgsql-session.php
/*
セッションテーブル:各sessionデータベースにあらかじめ作成
CREATE TABLE php_session ( |
| session_id |
varchar(32) |
NOT NULL, |
i_created |
integer |
NOT NULL, |
i_active |
integer |
NOT NULL, |
t_remote_addr |
varchar(20) |
DEFAULT '', |
t_session_data |
text, |
PRIMARY KEY (session_id) |
);
*/
define('SESS_SV', 2); // セッションを保存するDBの数
// DB接続文字列。実際にDBホスト名はsession0, session1 ... session#の形式となる
define('SESS_DB','dbname=session user=yohgaki host=session');
define('SESS_TABLE','php_session'); // セッション情報テーブル
// セッションセーブハンドラを登録
session_set_save_handler (
'pg_session_open',
'pg_session_close',
'pg_session_read',
'pg_session_write',
'pg_session_destroy',
'pg_session_gc'
);
function pg_session_open ($save_path, $session_name) {
return true;
}
function pg_session_close() {
global $session_db;
pg_query($session_db,'COMMIT;');
return true;
}
function pg_session_read ($session_id) {
global $session_db;
if (strlen($session_id) != 32) {
return '';
}
// セッションID(MD5)の最初の5文字のアスキー値の足し算の結果を
// 利用するセッションDBのモジュロを算出し負荷を分散
$sum = 0;
for($i=0; $i5; $i++) {
$sum += ord($session_id{$i});
}
$session_db = pg_pconnect(SESS_DB.$sum%SESS_SV);
$session_id = pg_escape_string($session_id);
$result = pg_query($session_db, 'BEGIN TRANSACTION; SELECT * FROM '. SESS_TABLE ." WHERE session_id = '$session_id' FOR UPDATE");
if (!pg_num_rows($result)) {
define('SESSION', false);
return '';
}
$sess = pg_fetch_assoc($result);
define('SESSION', true);
return $sess['t_session_data'];
}
function pg_session_write ($session_id, $session_data) {
global $session_db;
if (strlen($session_id) != 32) {
return false;
}
$session_id = pg_escape_string($session_id);
$session_data = pg_escape_string($session_data);
if (SESSION) {
$query = 'UPDATE '. SESS_TABLE ." SET i_active = ". time() .", t_session_data = '$session_data' WHERE session_id = '$session_id';";
} else {
$query = 'INSERT INTO '. SESS_TABLE ." (session_id, i_created, i_active, t_remote_addr, t_session_data) VALUES ('$session_id', ". time() .", ". time() .", '". $_SERVER['REMOTE_ADDR'] ."', '$session_data');";
}
pg_query($session_db,$query);
return true;
}
function pg_session_destroy ($session_id) {
global $session_db;
pg_query($session_db, 'DELETE FROM '. SESS_TABLE ." WHERE session_id = '". pg_escape_string($session_id). "'");
if (pg_errormessage($session_db)) {
return false;
}
return true;
}
function pg_session_gc ($maxlifetime = 4000) {
global $session_db;
pg_query($session_db, 'DELETE FROM '. SESS_TABLE ." WHERE i_active ". (time() - $maxlifetime));
if (pg_errormessage($session_db)) {
return false;
}
return true;
}
?>
|
セッションセーブハンドラのコードは、通常のセッションセーブハンドラとほぼ同じです。違う部分は、負荷分散を行うために利用するセッションデータベースを、セッションIDを基に算出している部分(リスト3の白文字)のみです。リスト4にその部分を抜粋します。
リスト4:セッションデータベースの算出部分
// セッションID(MD5)の最初の5文字のアスキー値の足し算の結果を
// 利用するセッションDBのモジュロを算出し負荷を分散
$sum = 0;
for($i=0; $i5; $i++) {
$sum += ord($session_id{$i});
}
$session_db = pg_pconnect(SESS_DB.$sum%SESS_SV);
Webサーバーを追加し、セッションデータベースのスケールアウトも必要となった場合にも、セッションデータを保存するデータベースは、必要に応じて追加可能です。
次回からはキャッシュについて解説していきます。