星期二, 10月 30, 2007

FreeBSD 的 mod_extract_forwarded2

一般的 reverse proxy 都不是 reverse NAT,所以 Apache 端必須要做一些處理才能拿到正確的使用者 IP Address,一般都是以 欄位做為判斷的標準,不過這樣的缺點是 PHP 必需要自己判斷。

就是直接把可信任來源的 替代成使用者端的 IP Address,如此一來 的程式不需要改變程式碼,與以前一樣只需要用 $_SERVER["REMOTE_ADDR"] 就可以取得使用者的 IP Address。

下的 安裝完後其實是有問題的,安裝完後的設定是:
LoadModule extract_forwarded2_module libexec/apache22/mod_extract_forwarded2.so

但應該改成:
LoadModule extract_forwarded_module libexec/apache22/mod_extract_forwarded2.so

然後對 reverse proxy 的 IP Address 設定:
AddAcceptForwarder 10.0.0.100

你可以寫個小程式,從不同 IP Address 帶 X-Forwarded-For 參數,將 $_SERVER["REMOTE_ADDR"] 印出測試看看。

Powered by ScribeFire.

星期日, 10月 14, 2007

AMR codec (3gp 在 ffmpeg 裡的支援問題)

剛剛發現,現在想要讓 支援 3gp 已經不需要自己 patch 了,而可以安裝 後直接在 compile 時使用。

星期一, 10月 01, 2007

PHP 內的 extract()

這陣子在 trace 程式碼的時候發現公司裡面的 Programmer 對於 extract() 的用法似乎不是很熟悉。

以往我們會用 foreach$_POST 的變數複製到全域變數上:
foreach ($_POST as $k => $v)
${"pvar_$k"} = $v;

但實際上 有現成的函數可以用:
extract($_POST, EXTR_PREFIX_ALL, "pvar");
extract($_GET, EXTR_PREFIX_ALL, "gvar");
extract($_COOKIE, EXTR_PREFIX_ALL, "cvar");

這樣就可以用 $pvar_variable 取得 $_POST["variable"] 的值。

星期六, 7月 14, 2007

在 DBIx::Class 內的兩項功能

兩個功能,第一個是比較複雜的 SQL query,第二個是自訂函數。

第一個功能是透過 scalar reference 做的,像是想要下:
UPDATE mytable SET viewnum = viewnum + 1 WHERE id = 123;

這樣的指令,那麼就用:
my $element = $c->model('MyDB')->find(123);
$element->update({viewnum => \'viewnum + 1'})

這個問題還蠻常在 的 mailing list 上被問,原因在於這是 的範疇,所以沒仔細看完文件不太會知道這個用法。

第二個功能其實還蠻重要的,因為有些 complex operation 沒辦法放到 Trigger 時得在 Database Library 這層做,其實是個蠻常見的情況,但我找了不少說明文件都沒有提到解法。後來發現其實在 就有提供了,不過因為這個功能還是 EXPERIMENTAL,所以大多數的文件都沒有講。

我建議用自訂函數後要把 的 mailing list 訂起來看,之後的版本可能是以其他方式解決這個問題。

星期五, 3月 30, 2007

C/PHP 的單一出口

PHP 裡面看到類似這樣的程式碼:
if (isset($_GET['var1'])) {
$var1 = $_GET['var1'];
if (isset($_GET['var2'])) {
$var2 = $_GET['var2'];
if (isset($_GET['var3'])) {
$var3 = $_GET['var3'];
// do something...
}
}
}

如果條件式再多一點的話就會更爆炸,我看到的 code 是七層... -_-

其實早在 C 語言時這個問題就有一個堪用的解法,拿到 PHP 上用也可以:
do {
if (isset($_GET['var1']))
break;
$var1 = $_GET['var1'];

// 以下類推
} while (0);

星期三, 3月 28, 2007

IE 裡 XMLHttpRequest 的 cache

在 IE 裡使用 XMLHttpRequest 時會被 cache (沒有遵守 Cache-Control 的 no-cache 參數),這個問題不會太大 (因為很好解,解法也不會破壞其他遵守規定的 Browser),解法是在 Cache-Control 外多送出幾組 header:
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Expires: Mon, 1 Mon 1990 00:00:00 GMT
Last-Modified: 現在的時間

這樣在 IE 上就會正常了。

星期日, 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 的味道...