星期日, 2月 25, 2007

Session 的瓶頸與解決方法

Session 的使用 這篇最後面提到了當 PHP server 超過一台時會有問題,這有蠻多的解法,不同的解法偏重在解決不同的問題。

第一種解法是修改 PHP Session 存放目錄,指到 NFS 路徑上,這樣就不會有在 web1 登入後 web2 讀不到的問題。這個在 交大資工 的 PHP server 就是這樣做,這樣做很明顯的優點是「簡單」,缺點就是 NFS 效率與穩定性的問題,不過對於小量的網站來說其實還不錯,因為第一次擴充時的瓶頸通常都是先出現在前端的 PHP server 上,NFS 的效率反而不是重點。

第二種解法則是透過 memcached 來放,這種方法通常都是因為 NFS 效率影響太大,或是覺得 NFS 的穩定性不夠,把 NFS 換成 memcached 希望能解決掉這個問題。這個作法需要利用 session_set_handle() 設定自己寫 (或是網路上抓來的) 程式碼將 Session 資料丟到 memcached 裡。另外一個方案是 Mohawk Software Session Handler Functions,沒有實際用過,不過看起來也是這一類的方案。

第三種解法是透過密碼學的方式將資料放在使用者端。如果資料不敏感 (像是只有使用者的 username),就透過 HMAC 簽,然後在使用者的瀏覽器上設 Cookie 紀錄,其他的頁面就可以利用 HMAC 檢查是不是自己簽的。如果資料比較敏感,不方便給使用者看到,那麼先透過 Cipher 加密後再用 HMAC 簽,然後再塞到使用者端瀏覽器的 Cookie。這種方式的好處是 Server 端是處於 Stateless 的狀態,這種方式等於完全不需要在 Server 端存資訊。但缺點就是 HMACCipher 用的 key 如果外洩就糟糕了。

第四種解法是透過硬體或是某些特定的 Reverse Proxy 解決 Load Balance 時 Session 所產生的問題,簡單來說就是某個 Cookie 一樣就會導到後端一樣的機器 (在這個例子裡是 PHPSESSID),於是後端就可以用最原始的 /tmp 來放 Session 資料。

這四種解法都有優缺點,並沒有哪個絕對比較好或是比較差,好壞要看使用的環境而定。

Session 的使用

Session 一直是網站設計裡面很重要的一個重點,有時候是因為資料不能放在使用者端的 Cookie,怕被使用者修改,另外一種原因是資料根本就不能給使用者知道。

像是 PHP 提供的 Session 的基本運作原理是透過 Cookie 設定一串十六進位的數字給 PHPSESSID,然後在 /tmp 下放對應的 sess_[PHPSESSID] 檔案,檔案裡就放 Session Variable。

舉個例子來說,User Login 後我們會通常會這樣寫:

// 假設前面已經確認身份了
session_start();
$_SESSION['user'] = $username; // 設定 $username 到 Session 裡

然後在其他檔案這樣檢查:

session_start();
if (isset($_SESSION['user'])) {
// 如果有 user 這個變數的話 blah blah...
}

對於小規模的 Web server 這樣做相當完美,因為使用者的 PHPSESSID 只是一個亂數對應到 /tmp 下的某個真正存放 Session 的檔案,使用者沒有辦法隨便亂改。同時使用者也沒有辦法猜出其他的人使用的 PHPSESSID。(當然這裡的前提是 PHP 的 Sessoion 模組寫的夠好,不會讓人用其他方法「猜」出來)

但當網站不斷的成長後,就會遭遇到一個問題:假設我有兩台 PHP server,這兩台存放 PHP Session 資料的 /tmp 當然是分開的。如果使用者在 web1 登入後,下一個連線是到 web2 上面,在 web2 上的 PHP 就沒辦法認出使用者登入過了。

這些問題留在下一篇提出一些解法。

星期三, 2月 14, 2007

HyperEstraier 大小限制

剛剛在轉資料的時候發現 HyperEstraier 的 node 沒辦法處理超過 4GB 的 database,所以必須建立多個 node 來放,再透過 setlink 將各節點串起來。

不是什麼大問題,不過 trace 了老半天才發現...

HyperEstraier 的速度問題

這是拿 strace 仔細追 estmaster 的時候發現的問題,用的參數是 strace -o output.txt -p PID -ttt。(沒有用 ktrace 是因為 kernel 沒編進去)

主要的問題有兩個,影響比較大的是在 estmaster.c 裡面有一堆 est_usleep() 造成不必要的 nanosleep(),這個部份可以簡單的 comment 掉加速。

另外一個問題是在 est_sock_recv_line() 這個 function 裡面,讀取一行的方法是一次讀 1 byte,這會使得速度被拖慢 (雖然沒有上一個多),不過這個部份得改寫所有透過 socket 處理的 code,並不是那麼容易生出 patch...

總而言之, 頗有 woju 的味道...