09.16
我最近寫了一隻小程式,原本是今年學期末就要寫好的,因為早就答應人家了,不過我暑假(暑修)實在太忙加上想說既然要寫,就寫的完整一點,所以拖到現在才完成這隻程式。
SiteStates 是個 多功能 簡單的計數器, 也可以稱為探測器 或是 間諜,程式部份大概幾百行而已,所以只能算是小玩具,它主要是可以統計 訪客人數 和 線上人數 以及顯示 到訪紀錄
其實要寫一個會動的訪客記錄器,對一般會寫的人來說,大概是一小時或是幾十分鐘的事情。因為寫出這類的東西並不困難,但是週邊一堆困擾的事情,倒是很麻煩,從介面的修改,資料庫的規劃(和以後的可擴充性)..等等。
不過在寫這隻程式的時候,我還是遇到了一些瓶頸,主要是牽扯到效能的問題,這邊和大家分享一下。
取得同時在線人數的方法?
建立一個 table ,裡面設定三個欄位, id 、 ip 以及 lastvisit 。id 是使用者的代號,ip 則是訪客來源, lastvisit 則是 unix 的 timestamp 。如此遇到訪客的時候,先檢查有沒有存在,如果沒有,就 INSERT 一筆資料進去,如果存在,就把原本的那筆資料 UPDATE ,然後每次跑這個 script ,都先把 timestamp 過期的 records 刪除。
我曾經在 PTT 的 PHP 板提出作法如下
建立一個 table 叫做 online 然後以下欄位:
serial - 序號.自動增加
id - 屬於哪個站台的紀錄
ip - 進入的使用者來源
lastvisit - 進入時間
首先刪除所有逾時的人 (假設 300 秒逾時)
$sql = "DELETE FROM online WHERE unix_timestamp() - lastvisit >= 300 AND id = '$id'";
使用者是否造訪過
$sql = "SELECT id FROM online WHERE ip = '$ip' AND id = '$id'";
計算 mysql_query($sql) 是為 0
如果是 0 的話,插入一筆新的資料:
$sql = "INSERT INTO online (id, ip, lastvisit) VALUES ('$id','$ip',unix_timestamp())";
如果不是 0 的話,把 time stamp 延後
$sql = "UPDATE users_online SET lastvisit = unix_timestamp() WHERE id = '$id' AND ip = '$ip'";
最後計算該站台有多少人
$sql = "SELECT count(id) FROM online WHERE id='$id'";
另外有位 allanshen 兄,回覆了一個更好的作法:
建立一個 table - online 有3個欄位 id、ip、lastvisit,id 跟 ip 合併設定為 unique key
刪除所有逾時的人跟您的方式一樣
然後使用者到訪時用
$sql = "REPLACE INTO online (id,ip,lastvisit) VALUES ('$id','$ip',unix_timestamp())";
計算人數一樣用 $sql = "select count(id) FROM online where id='$id'";
這樣跟您原來的方式省了一次 select
儘在此再次謝謝他的意見。
取得 PageRank 的方法?
Google Code 裡面有一個叫做 Popstats 的函式庫,目前是可以動的,根據 Wildcat 哥的可靠消息, PHP Google PageRank Calculator 已經無法運作了。
到訪紀錄的 Maintain?
這是我最頭痛的事情,不過還好也是迎刃而解。原本我想了複雜的方法,但是 wildcat 哥和我說了個比較簡單的作法:
wildcat 說:
喔,我會建議你把來源用 text or blob 存,反正一百筆大概也才 15x100 = 1600 , + 100(分隔號) = 1600 bytes,這種大小的字串給 php 處理其實非常非常迅速…(大概就長這樣 192.168.100.100|192.168.200.200|.... ) 要讀出來的時候再把它 split or explode 就好了。
實際作法大致如下:
/*
建立一個叫做 users 的 table :
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL auto_increment,
`url` text NOT NULL,
`password` varchar(32) NOT NULL,
`from_ip` text,
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
*/
function add_ip_to_user($id, $ip) /* 新增一筆 ip 紀錄 */
{
require('database.php');
$sql = "SELECT from_ip FROM users WHERE id = $id";
$result = lab3_query($sql);
list($from_ip) = mysql_fetch_row($result);
$array = array();
$array = unserialize($from_ip);
$size = count($array);
if($from_ip == '')
{
$array[0][0] = gethostbyaddr($ip);
$array[0][1] = date('Y-m-d H:i:s');
$array[0][2] = $ip;
}
else
{
$array[$size][0] = gethostbyaddr($ip);
$array[$size][1] = date('Y-m-d H:i:s');
$array[$size][2] = $ip;
}
if($size >= 500) /* 最多幾筆 */
array_shift($array);
$from_ip = serialize($array);
$sql = "UPDATE users SET from_ip = '$from_ip' WHERE id = $id";
lab3_query($sql);
}
用 serialize()、unserialize() 或是 implode()、 explode() 來搭配存取 from_ip 這個 text 欄位。以上大概是比較需要討論的地方,其他需要具備的條件就是對於繪圖函式的使用、陣列處理、以及一般字串處理和網路函式(像是取得 IP 和 DNS 反解)的應用,另外 CSS 和 HTML 的技術則是基本必須具備的,這邊著重於 PHP 和資料庫處理的部份,所以使用者介面的部份我就不提了。
如果有建議或是問題也很歡迎在此提出,非常感謝!

[...] 1. SiteStates 網站 2. SiteStates 計數器 探測器 間諜 小玩具 訪客人數 in roga’s blog. « 新電腦 [...]
其實我會建議不要在寫入資料庫時就做gethostbyaddr,
遇到沒辦法反查的ip時寫入的動作就被lock在那邊 XD
應該是讀出來要顯示的時候再去作就好了 :p
還OK啦..
查不出來它就直接 return IP 囉
雖然我不會寫這個程序,
直接去你計算器網頁拿來用了。
我們馬來西亞整個“衣服部落格”的使用者都非常感謝你!!
衣服部落格的管理者您好:
不用客氣,很高興有這個機會能幫上忙,如果有程式上的問題或是系統方面的心得,也很歡迎討論交流,我對這些都相當有興趣
重點是等他 reuturn 是要時間的 XD
之前 PageRank = 0 的時候沒有 cache 的才慘。她會先去跟 Google 問三次,如果都 fail 才 return FALSE 。
一整個速度被拖下來,還好我把程式改寫一下,才解決這問題..
我發現在
if($size >= 500) /* 最多幾筆 */
array_shift($array);
這邊有問題.. = =