Thứ Tư, 9 tháng 2, 2011

Sử dụng session_set_save_handler để override session handle

aViệc scale (vertical) ứng dụng web sử dụng php  , có một vấn đề cần quan tâm hàng đầu là vấn đề sử lý session .

Khi scale php sẽ chạy trên các máy chủ khác nhau , vậy làm thế nào để các user khi login web app mà dùng chung được session trên nhiều server ?

Để làm được điều này chúng ta cần xử lý sao cho tất cả các máy chủ đều sử dụng chung một hệ thống quản lý session .

Hoặc đơn giản là sử dụng chung một session storage .

Mặc định trong php sẽ sử dụng file session handle các file chứa session sẽ nằm trong /tmp của mỗi server .

Điều này có thể gợi ý cho chúng ta việc share chung 1 vùng lưu trữ (ví dụ dùng NFS để share chung /tmp giữa các máy chủ).

Tuy nhiên cách này lại ảnh hưởng đến tốc độ truy xuất (vì phải qua mạng - sau đó là đến việc máy host session sẽ quá tải diskio).

Một hướng khác là sử dụng một db mysql chung để lưu session , thực tế thì cách này tạm được .

Để làm như vậy chúng ta cần override lại các hàm xử lý session của php
session_set_save_handler("open", "close", "read", "write", "destroy", "gc");

Sử dụng hàm session_set_save_handler để trỏ các call back function cần thiết , như vậy chúng ta cần viết lại 6 hàm open, close , read ,write ,destroy , gc .

tạm thời chúng ta viết tạm các hàm như sau :
<?php
function open($save_path, $session_name)
{
echo "open";
}

function close()
{
echo "close";
}

function read($id)
{
echo "read";
}

function write($id, $sess_data)
{
echo "write";

}

function destroy($id)
{
echo "destroy";
}

function gc($maxlifetime)
{
echo "gc";
}

session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
// test thử phát
session_start();

?>

Chạy thử . kết quả : openread writeclose ...

Như vậy chúng ta thấy thứ tự của một quá trình đọc session .

Việc tiếp theo là implement mấy hàm trên vào mysql , memcached , file , hay bất kỳ một storage nào có thể dùng chung được là xong .

với file (cái này chỉ là làm lại những gì php đã làm :D )
<?php
$sess_save_path=".";
function open($save_path, $session_name)
{
global $sess_save_path;

$sess_save_path = $save_path;
return(true);
}

function close()
{
return(true);
}

function read($id)
{
global $sess_save_path;

$sess_file = "$sess_save_path/sess_$id";
return (string) @file_get_contents($sess_file);
}

function write($id, $sess_data)
{
global $sess_save_path;

$sess_file = "$sess_save_path/sess_$id";
if ($fp = @fopen($sess_file, "w")) {
$return = fwrite($fp, $sess_data);
fclose($fp);
return $return;
} else {
return(false);
}

}

function destroy($id)
{
global $sess_save_path;

$sess_file = "$sess_save_path/sess_$id";
return(@unlink($sess_file));
}

function gc($maxlifetime)
{
global $sess_save_path;

foreach (glob("$sess_save_path/sess_*") as $filename) {
if (filemtime($filename) + $maxlifetime < time()) {
@unlink($filename);
}
}
return true;
}

session_set_save_handler("open", "close", "read", "write", "destroy", "gc");

//test
session_start();

?>

Hay với mysql đại khái thế này :
function read($id)
{
$sql = "select * from `tb_session` where `session_id`=$id";
mysql_query($sql);
//....
}

function write($id, $sess_data)
{
$sql = "INSERT INTO `tb_session` (`session_id`, `updated_on`) VALUES ('{$this->session_id}', NOW())";
mysql_query($sql);

}

function destroy($id)
{
$sql = "Delete from `tb_session` where `session_id`=$id";
mysql_query($sql);
}

Hay với memcached :
function read($id)
{
return memcached::get("sessions/{$id}");
}

function write($id, $data)
{
return memcached::set("sessions/{$id}", $data, 3600);
}

function destroy($id)
{
return memcached::delete("sessions/{$id}");
}

Dùng memcache hay mysql cũng khá nhanh , mysql thì chạy bàng memory storage engine , bật query cache tướng đối lớn cho nhanh , có thể replication ra nhiều server để đẩy performed lên cao nữa nếu tải lớn .

3 nhận xét:

  1. Dòng return memcached::set(“sessions/{$id}”, $data, self::$lifetime); em có thấy cái class nào đâu mà có self::$lifetime vậy bác.

    Trả lờiXóa
  2. @datgs: class memcached là của php.

    Trả lờiXóa