1. 程式人生 > >ThinkPHP實現統計線上使用者的幾種方式

ThinkPHP實現統計線上使用者的幾種方式

關於統計線上使用者的功能。以前也做過,用的一些比較簡單的方法,但是缺點也很明顯:精確統計和伺服器、資料庫壓力之間要做出平衡。
所以想找一個既能精確統計又能不佔用太多伺服器資源的方法。先說說一些平常的做法:

一,每次使用者操作更新其線上時間

這個方法很直接,在使用者表裡加一個欄位update_time,每次使用者進行操作,都更新這個欄位為當前時間,一般是在一個被所有Action繼承的基類裡寫這個操作。
然後定義一個過期時間,比如10分鐘,表示10分鐘沒進行任何操作的使用者預設為不線上。這樣,統計當前線上使用者的sql語句大概是這樣

select count(*)from think_user where update_time
>now()-10*60

優點:實現簡單,通俗易懂
缺點:1,對“線上”的定義模糊,萬一使用者看一篇文章時間比較長,10分鐘內沒進行任何操作,他就被忽略了;2,如果user表資料量很大,那效率將極差

二,將線上使用者單獨放入一張表

對於方法一的改進。新建一張表think_inline,欄位有user_id、update_time,每次使用者操作時,先判斷表裡有沒有該使用者的記錄,沒有則新增,有就更新update_time。
並同時加上刪除失效資料操作

deletefrom think_inline where update_time

這樣,統計線上就可以直接count這張表就行了。而且這表的資料量不會很大(至少要比使用者表小的多)
優點:減少資料庫壓力
缺點:仍然對“線上”的定義不準確

三,用JS定時器

這個方法是綜合了一和二。新建一張表think_inline,也是在基類中定義每次使用者操作時更新時間,參考二的做法。
不同之處是,在每個html模板裡,加上一個js定時器,setInterval('updateTime', 10*3600);每隔10分鐘傳送一次ajax請求,更新update_time欄位。這樣,即使使用者在一個頁面停留時間過長,也不會被誤認為不線上了。並且可以通過減少請求的間隔,來增加精確度,當然了,對伺服器的壓力就更大了。
優點:對線上的判斷較為準確
缺點:仍然不能既精確又不增加伺服器壓力,必須在兩者之間進行取捨。

四,使用TP的SessionDb驅動進行最優化設計

這也就是網上有人說的session存入資料庫的方法,這種方法優點很多。具體做法是。。。

1,為什麼要將session存入資料庫?

session是儲存在伺服器的一組臨時資料。一般情況下,我們在做使用者登入時,會將使用者資料存入session。這樣,在任何頁面都可以方便呼叫,而且每個客戶端會產生唯一的session_id,不會混餚。並且在關閉瀏覽器後,伺服器會有session回收機制,自動刪除過期session。
這是session的優點:唯一性、方便呼叫、不會過多佔用資源。但是也有缺點:在客戶端是以cookie方式儲存的,禁用cookie就沒用了。
那麼,伺服器是如何存放session的呢?他是預設將session以檔案的方式儲存在硬碟上的。可是,對於我們碼農來說,操作資料庫要比讀檔案方便的多,並且可以對session資料進行各種操作。
而統計線上使用者人數就是通過統計有多少條session記錄來實現的。
2,如何把session存入資料庫?

TP的SessionDb驅動就實現了這個功能。原理就是通過改寫PHP預設的session操作來實現,核心函式session_set_save_handler(),有興趣的可以研究一下。該驅動將session的增、讀、取、和刪都放入了資料庫。
使用方法也很簡單:

1,建表,驅動的註釋裡的sql語句執行下就好
2,新增配置:

//Session配置'SESSION_TYPE'=>'db',//資料庫儲存session'SESSION_TABLE'=>'think_session',//存session的表'SESSION_EXPIRE'=>600,//session過期時間

這樣,只要我們在程式裡使用了session()函式,資料庫裡就會有記錄。

3,利用資料庫session實現統計線上使用者
1,統計線上總人數

$map = array('session_expire'=>array('gt',NOW_TIME));$inline = D('Session')->where($map)->count();

2,統計遊客(未登入)人數

$map = array('session_expire'=>array('gt',NOW_TIME),'session_data'=>array('eq',''));$huiyuan = D('Session')->where($map)->count();

3,統計會員(已登入)人數

$map = array('session_expire'=>array('gt',NOW_TIME),'session_data'=>array('neq',''));$huiyuan = D('Session')->where($map)->count();

4,判斷一個使用者是否線上。
在使用者表裡新增一個欄位:session_id。
(1)在登入操作裡,儲存該使用者的session_id,

$session_id = session_id();D('User')->where(array('id'=>$user_id))->save('session_id'=>$session_id);

(2)檢查session表裡是否存在該session_id,未過期並且有值,

$map = array('session_id'=>$session_id,'session_expire'=>array('gt',NOW_TIME),'session_data'=>array('neq',''));$res = D('Session')->where($map)->find();if($res){dump('該使用者線上。')}else{dump('該使用者不線上。')}

碼字太麻煩啦,先寫這麼多,後面總結該方法的幾大優點以及注意事項。

4,總結
1,實現步驟:使用者表新增欄位儲存session_id;使用TP的SessionDb驅動。是不是很簡單?
2,優點:
(1)比上面三種方法對資料庫和伺服器的壓力都小,操作簡單
(2)能夠區分線上使用者是會員還是遊客(session_data欄位是否有值),discuz就是這樣做的
(3)可以通過刪除某使用者的session記錄,實現將其“踢下線”的功能
3,缺點:
(1)仍然不能精確統計,只能說,XX秒內線上多少人
4,注意事項:
(1)由於該方法的SessionDb驅動必須使用session()函式才能觸發,所以必須配置自動開啟session(預設就是開啟)。TP在執行流程裡會使用session()函式,所以你什麼都不寫,session資料也會存入資料庫。
(2)因為資料庫的session資料是不會自己刪除的,所以對於過期的資料,必須呼叫session()方法才會刪。
這也就意味著,如果你的網站一個人都沒有訪問,那麼資料庫的過期session記錄會一直存在。
也就是由於這種機制,對於一些突發事件(使用者直接X瀏覽器、直接關機、發生地震……),在其關閉瀏覽器的一段時間(session過期時間)後,其他使用者對網站的訪問,會觸發session回收,刪除過期記錄。所以,就不怕統計出來的資料不準確了。
當然了,就算沒人訪問,你也可以在count時加上session_expire>time()來統計。

但是也有誤差,就是session過期時間,5分鐘後過期,就有5分鐘的誤差。時間設的越小,對資料庫的操作就越頻繁;越大,精確度就越低。
(3)這個session過期時間就意味著,如果使用者5分鐘內不進行任何操作,其就會自動退出登入。所以,為了使用者體驗好,請加上cookie自動登入功能。目前官網就是這麼做的。
(4)評論裡有人提到,驗證碼也會存入session,所以我們判斷的時候,就不能值統計有值的記錄了。
需要先獲取有值的資料,再判斷裡面有沒有儲存使用者資訊的引數名。雖然session_data欄位是用二進位制儲存的,但是查詢出來就是一個字串。
比如,我們用的user下標來儲存的使用者資訊,

session('user',$data);//使用者登入資訊//獲取真實會員數//查詢有值的session記錄$list = D('Session')->where(array('session_data'=>array('NEQ',''),'session_expire'=>array('lt',NOW_TIME)))->select();//判斷值裡是否有會員標識foreach($list as $k=>$value){if(strpos($value['session_data'],'user')===false){$count++;}dump($count);//真實會員人數}

(5)由於每種瀏覽器都有各自的session機制,所以,如果一個人在一臺電腦上同時開了5種瀏覽器,則資料庫會儲存5條不同的記錄
實際使用時,仍需要考慮搜尋引擎的誤差。在其抓取我們的頁面時,也會產生session。
五,最終結論

由於HTTP協議的限制:請求完成後就會斷開與客戶端的連線。所以實際上,

我們根本無法精確而實時地統計線上人數

!儘管有各種各樣的方法來增加統計的精確度,然而都是治標不治本。唯有放棄HTTP協議,使用“長連線”的連結方式,才能精確判斷使用者在還是離

關鍵字詞:ThinkPHP,線上統計,SESSION存表

原文連結:http://wbloc.com/index.php/article-50.html