PHP により、複数の Web サーバーが SESSION データを共有できるようになります (セッション データは mysql データベースに書き込まれます)
1. 問題の原因
少し大きなウェブサイトでは通常、複数のサーバーがあり、各サーバーは異なる機能を持つモジュールを実行し、異なる第 2 レベルのドメイン名を使用します。しかし、総合的なウェブサイトでは、ユーザー システム、つまりユーザー名、パスワードのセットが統一されています。ウェブサイト全体の各モジュールにログインするために使用できます。各サーバーがユーザー データを共有するのは比較的簡単で、バックエンドにデータベース サーバーを配置するだけで済み、各サーバーは統一されたインターフェイスを通じてユーザー データにアクセスできます。しかし、まだ問題があります。つまり、ユーザーがこのサーバーにログインした後、別のサーバーの他のモジュールに入るときに、再度ログインする必要があります。これは 1 回限りのログインであり、一般的な問題はすべて にマッピングされています。実際には、さまざまなサーバー間で SESSION データを共有する方法です。
2. PHP SESSIONの仕組み
問題を解決する前に、まずPHP SESSIONがどのように機能するかを理解しましょう。クライアント (ブラウザなど) が Web サイトにログインすると、訪問した PHP ページは session_start() を使用して SESSION を開くことができ、これによりクライアントの一意の識別 SESSION ID が生成されます (この ID は関数 session_id( ))。 SESSION ID は 2 つの方法でクライアントに保持できるため、別のページをリクエストするときに PHP プログラムがクライアントの SESSION ID を学習できます。1 つは、SESSION ID を GET URL に自動的に追加する方法です (これは UNIX でのみ実行できます)。システム) (Windows システムはこれを URL に自動的に追加できません)。デフォルトでは、変数名は PHPSESSID です。もう 1 つは、COOKIE を通じてセッション ID を保存することです。この Cookie は PHPSESSID です。ここでは、広く使われている COOKIE メソッドを中心に説明します。
では、SESSIONのデータはどこに保存されているのでしょうか? もちろんサーバー側にはありますが、メモリではなくファイルやデータベースに保存されます。デフォルトでは、php.ini に設定されている SESSION 保存方法は
Files(session.save_handler = files)、つまりファイルの読み書きによってSESSIONデータを保存し、SESSIONファイルが保存されるディレクトリはsession.save_pathで指定され、ファイル名は
で始まります。sess_ はプレフィックスで、その後に SESSION ID が続きます (例: sess_c72665af28a8b14c0fe11afe3b59b51b)。ファイル内のデータはシリアル化後の SESSION データです。アクセス数が多いと発生する可能性があります
この場合、SESSION ファイルを保存するための階層ディレクトリを設定できます。設定方法は、session.save_path="N;/save_path" です。階層レベルの数
save_path は開始ディレクトリです。 SESSION データを書き込むとき、PHP はクライアントの SESSION_ID を取得し、この SESSION ID に基づいて指定された SESSION ファイル保存ディレクトリ内で SESSION_ID を検索します
対応するSESSIONファイルが存在しない場合は作成され、最終的にデータがシリアル化されてファイルに書き込まれます。 SESSION データの読み取りも同様の操作プロセスです。読み取りデータはデシリアライズされ、それに応じて生成される必要があります。
SESSION変数。マルチサーバー共有SESSIONの主な障害と解決策
SESSION の動作原理を理解すると、デフォルトでは、各サーバーが同じクライアントに対してそれぞれ SESSION ID を生成することがわかります。たとえば、同じユーザーのブラウザーの場合、サーバー A によって生成される SESSION ID は 30de1e9de3192ba6ce2992d27a1b6a0a ですが、B によって生成されます。サーバーは c72665af28a8b14c0fe11afe3b59b51b を生成します。また、PHP の SESSION データは、このサーバーのファイル システムに別途保存されます。
問題を特定したら、解決を開始できます。 SESSION データを共有したい場合は、次の 2 つの目標を達成する必要があります:
1 つは、同じクライアントに対して各サーバーによって生成される SESSION ID は同じである必要があり、同じ COOKIE を介して渡すことができるということです。つまり、各サーバーは PHPSESSID という名前の同じ COOKIE を読み取ることができなければなりません。
もう一つは、SESSIONデータの保存方法/場所は、各サーバーがアクセスできることを保証する必要があるということです。 簡単に言えば、複数のサーバーはクライアントの SESSION ID を共有し、サーバーの SESSION も共有する必要があります
データ。
最初の目標の実現は、実際には、COOKIE のドメインを特別に設定するだけです。デフォルトでは、COOKIE のドメインは、現在のサーバーのドメイン名/IP アドレスになります。
2つのサーバーで設定されたCOOKIESは相互にアクセスできません。
4. コードの実装
まずデータテーブルを作成します。MySQLのSQL文は以下の通りです。
CREATE TABLE `sess` (`sesskey` varchar(32) NOT NULL デフォルト ,
`expiry` bigint(20) NOT NULL デフォルト 0,
`data` ロングテキスト NOT NULL,
PRIMARY KEY (`sesskey`)、KEY `expiry` (`expiry`)
) TYPE=MyISAM
sesskeyはSESSION ID、expiryはSESSIONの有効期限、dataはSESSIONデータの保存に使用されます。
デフォルトでは、SESSIONデータはファイルモードで保存されますが、データベースモードで保存したい場合は、各SESSIONオペレーションの処理関数を再定義する必要があります。 PHP は session_set_save_handle() を提供します
関数、この関数を使用して SESSION 処理プロセスをカスタマイズできます。もちろん、最初に session.save_handler を PHP で設定できる user に変更する必要があります: session_module_name(user);
次に、session_set_save_handle() 関数に注目してみましょう。
この関数には 6 つのパラメータがあります: session_set_save_handler (string open, string close, string read, string write, string destroy, string gc) 各パラメータは各操作の関数名です:
開く、閉じる、読み取る、書き込む、破棄する、ガベージコレクション。詳細な例は PHP マニュアルにあります
ここではOOを使ってこれらの操作を実装します。詳細なコードは次のとおりです。
define(MY_SESS_TIME,3600); //SESSION 生存時間
;
//クラス定義クラスMy_Sess
{
/**
※データベース接続オブジェクトが静的変数として設定されているため、他のメソッドでデータベース接続オブジェクトを呼び出すことができない理由はまだ不明です
。
** @var obj
*/
static public $db;
/**
*コンストラクター
*
* @param obj $dbname データベース接続オブジェクト
*/
関数__construct($dbname){
self::$db = $dbname;
}
/**
* セッションを初期化し、データベース mysql を使用してセッションの値を保存し、session_set_save_handler メソッドを使用して実装します
*
*/
関数init()
{
$ドメイン = ;
//GET/POST変数メソッドは使用しないでください
ini_set(session.use_trans_sid,0);
//ガベージコレクションの最大有効期間を設定します
ini_set(session.gc_maxlifetime,MY_SESS_TIME);
//COOKIEを使用してSESSION IDを保存する方法
ini_set(session.use_cookies,1);
ini_set(session.cookie_path,/);
//SESSION IDを保存するCOOKIEを複数のホストで共有する ローカルサーバーでテストしているため、$domain=
と設定します。
ini_set(session.cookie_domain,$domain);//デフォルトのファイルではなく、session.save_handlerをuserに設定します
セッションモジュール名(ユーザー);
//SESSIONの各操作に対応するメソッド名を定義する
session_set_save_handler(
array(My_Sess,open),//My_Sessクラスのopen()メソッドに相当、以下同様。
array(My_Sess,close),
array(My_Sess,read),
array(My_Sess,write),
array(My_Sess,destroy),
配列(My_Sess,gc)
);
//session_start() は session_set_save_handler メソッドの後に配置する必要があります
session_start();
}
関数 open($save_path, $session_name) {
//print_r($sesskey);
true を返す;
} //関数の終了
関数 close(){
if(self::$db){
self::$db->close();
}
true を返す;
}
関数 read($sesskey) {
$sql = SELECT `data` FROM `sess` WHERE `sesskey`= . (self::$db->qstr($sesskey)) AND `expiry`>= .
$rs=self::$db->execute($sql);
if($rs){
if($rs->EOF){
戻る ;
} else {//SESSION ID に対応する SESSION データを読み取ります
$v = $rs->fields[0];
$rs->close();
$v を返します;
}
}
戻る ;
}
関数write($sesskey,$data){
$qkey = $sesskey;
$expiry = time()+MY_SESS_TIME;
$arr = array(
sesskey => $qkey,
有効期限 => $有効期限,
データ => $data);
self::$db->replace(sess, $arr, sesskey, true);
true を返す;
}
関数 destroy($sesskey) {
$sql = DELETE FROM `sess` WHERE `sesskey`=.self::$db->qstr($sesskey);
$rs =self::$db->execute($sql);
true を返す;
}
関数 gc($maxlifetime = null) {
$sql = DELETE FROM `sess` WHERE `expiry`<.time();
self::$db->execute($sql);
// テーブルsessの削除操作が頻繁に行われるため、断片化が発生しやすくなります、
//そこで、ガベージコレクション中にテーブルを最適化します。
$sql = テーブルを最適化する `sess`;
self::$db->Execute($sql);