先說說一些平常的做法:
一,每次用戶操作更新其在線時(shí)間
這個(gè)方法很直接,在用戶表里加一個(gè)字段update_time,每次用戶進(jìn)行操作攻走,都更新這個(gè)字段為當(dāng)前時(shí)間,一般是在一個(gè)被所有Action繼承的基類里寫這個(gè)操作此再。
然后定義一個(gè)過期時(shí)間陋气,比如10分鐘,表示10分鐘沒進(jìn)行任何操作的用戶默認(rèn)為不在線引润。這樣巩趁,統(tǒng)計(jì)當(dāng)前在線用戶的sql語句大概是這樣selectcount(*)fromthink_userwhereupdate_time>now()-10*60
復(fù)制代碼優(yōu)點(diǎn):實(shí)現(xiàn)簡單,通俗易懂
缺點(diǎn):1淳附,對“在線”的定義模糊议慰,萬一用戶看一篇文章時(shí)間比較長,10分鐘內(nèi)沒進(jìn)行任何操作奴曙,他就被忽略了别凹;2,如果user表數(shù)據(jù)量很大洽糟,那效率將極差
二炉菲,將在線用戶單獨(dú)放入一張表
對于方法一的改進(jìn)。新建一張表think_inline坤溃,字段有user_id拍霜、update_time,每次用戶操作時(shí)薪介,先判斷表里有沒有該用戶的記錄祠饺,沒有則新增,有就更新update_time汁政。
并同時(shí)加上刪除失效數(shù)據(jù)操作deletefromthink_inlinewhereupdate_time
復(fù)制代碼這樣道偷,統(tǒng)計(jì)在線就可以直接count這張表就行了缀旁。而且這表的數(shù)據(jù)量不會很大(至少要比用戶表小的多)
優(yōu)點(diǎn):減少數(shù)據(jù)庫壓力
缺點(diǎn):仍然對“在線”的定義不準(zhǔn)確
三,用JS定時(shí)器
這個(gè)方法是綜合了一和二勺鸦。新建一張表think_inline并巍,也是在基類中定義每次用戶操作時(shí)更新時(shí)間,參考二的做法换途。
不同之處是履澳,在每個(gè)html模板里,加上一個(gè)js定時(shí)器怀跛,setInterval('updateTime', 10*3600);每隔10分鐘發(fā)送一次ajax請求,更新update_time字段柄冲。這樣吻谋,即使用戶在一個(gè)頁面停留時(shí)間過長,也不會被誤認(rèn)為不在線了现横。并且可以通過減少請求的間隔漓拾,來增加精確度,當(dāng)然了戒祠,對服務(wù)器的壓力就更大了骇两。
優(yōu)點(diǎn):對在線的判斷較為準(zhǔn)確
缺點(diǎn):仍然不能既精確又不增加服務(wù)器壓力,必須在兩者之間進(jìn)行取舍姜盈。
四低千,使用TP的SessionDb驅(qū)動進(jìn)行最優(yōu)化設(shè)計(jì)
這也就是網(wǎng)上有人說的session存入數(shù)據(jù)庫的方法,這種方法優(yōu)點(diǎn)很多馏颂。目前示血,LZ就是用的這個(gè)。
具體做法是救拉。难审。。有50個(gè)評論亿絮,我就公布告喊,當(dāng)然了,跟百度的那些復(fù)制粘貼的例子不一樣派昧,會有深入解析哦黔姜!
沒多少人看啊,先寫一些蒂萎。
1地淀,為什么要將session存入數(shù)據(jù)庫?
session是存儲在服務(wù)器的一組臨時(shí)數(shù)據(jù)岖是。一般情況下帮毁,我們在做用戶登錄時(shí)实苞,會將用戶數(shù)據(jù)存入session。這樣烈疚,在任何頁面都可以方便調(diào)用黔牵,而且每個(gè)客戶端會產(chǎn)生唯一的session_id,不會混肴爷肝。并且在關(guān)閉瀏覽器后猾浦,服務(wù)器會有session回收機(jī)制,自動刪除過期session灯抛。
這是session的優(yōu)點(diǎn):唯一性金赦、方便調(diào)用、不會過多占用資源对嚼。但是也有缺點(diǎn):在客戶端是以cookie方式保存的夹抗,禁用cookie就沒用了。
那么纵竖,服務(wù)器是如何存放session的呢漠烧?他是默認(rèn)將session以文件的方式保存在硬盤上的∶移觯可是已脓,對于我們碼農(nóng)來說,操作數(shù)據(jù)庫要比讀文件方便的多通殃,并且可以對session數(shù)據(jù)進(jìn)行各種操作度液。
而統(tǒng)計(jì)在線用戶人數(shù)就是通過統(tǒng)計(jì)有多少條session記錄來實(shí)現(xiàn)的。
2画舌,如何把session存入數(shù)據(jù)庫恨诱?
TP的SessionDb驅(qū)動就實(shí)現(xiàn)了這個(gè)功能。原理就是通過改寫PHP默認(rèn)的session操作來實(shí)現(xiàn)骗炉,核心函數(shù)session_set_save_handler()照宝,有興趣的可以研究一下。該驅(qū)動將session的增句葵、讀厕鹃、取、和刪都放入了數(shù)據(jù)庫乍丈。
使用方法也很簡單:1剂碴,建表,驅(qū)動的注釋里的sql語句運(yùn)行下就好
2轻专,添加配置://Session配置
'SESSION_TYPE'=>'db',//數(shù)據(jù)庫存儲session
'SESSION_TABLE'=>'think_session',//存session的表
'SESSION_EXPIRE'=>600,//session過期時(shí)間
復(fù)制代碼這樣忆矛,只要我們在程序里使用了session()函數(shù),數(shù)據(jù)庫里就會有記錄。
3催训,利用數(shù)據(jù)庫session實(shí)現(xiàn)統(tǒng)計(jì)在線用戶
1洽议,統(tǒng)計(jì)在線總?cè)藬?shù)$map=array('session_expire'=>array('gt',NOW_TIME));
$inline=D('Session')->where($map)->count();
復(fù)制代碼2,統(tǒng)計(jì)游客(未登錄)人數(shù)$map=array('session_expire'=>array('gt',NOW_TIME),'session_data'=>array('eq',''));
$huiyuan=D('Session')->where($map)->count();
復(fù)制代碼3漫拭,統(tǒng)計(jì)會員(已登錄)人數(shù)$map=array('session_expire'=>array('gt',NOW_TIME),'session_data'=>array('neq',''));
$huiyuan=D('Session')->where($map)->count();
復(fù)制代碼4亚兄,判斷一個(gè)用戶是否在線。
在用戶表里新增一個(gè)字段:session_id采驻。
(1)在登錄操作里审胚,保存該用戶的session_id,$session_id=session_id();
D('User')->where(array('id'=>$user_id))->save('session_id'=>$session_id);
復(fù)制代碼(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('該用戶不在線痘系。')}
復(fù)制代碼
碼字太麻煩啦菲嘴,先寫這么多,后面總結(jié)該方法的幾大優(yōu)點(diǎn)以及注意事項(xiàng)碎浇。
4,總結(jié)
1璃俗,實(shí)現(xiàn)步驟:用戶表新增字段保存session_id奴璃;使用TP的SessionDb驅(qū)動。是不是很簡單城豁?
2苟穆,優(yōu)點(diǎn):
(1)比上面三種方法對數(shù)據(jù)庫和服務(wù)器的壓力都小,操作簡單
(2)能夠區(qū)分在線用戶是會員還是游客(session_data字段是否有值)唱星,discuz就是這樣做的
(3)可以通過刪除某用戶的session記錄雳旅,實(shí)現(xiàn)將其“踢下線”的功能
3,缺點(diǎn):
(1)仍然不能精確統(tǒng)計(jì)间聊,只能說攒盈,XX秒內(nèi)在線多少人
4,注意事項(xiàng):
(1)由于該方法的SessionDb驅(qū)動必須使用session()函數(shù)才能觸發(fā)哎榴,所以必須配置自動開啟session(默認(rèn)就是開啟)型豁。TP在執(zhí)行流程里會使用session()函數(shù),所以你什么都不寫尚蝌,session數(shù)據(jù)也會存入數(shù)據(jù)庫迎变。
(2)因?yàn)閿?shù)據(jù)庫的session數(shù)據(jù)是不會自己刪除的,所以對于過期的數(shù)據(jù)飘言,必須調(diào)用session()方法才會刪衣形。
這也就意味著,如果你的網(wǎng)站一個(gè)人都沒有訪問姿鸿,那么數(shù)據(jù)庫的過期session記錄會一直存在谆吴。
也就是由于這種機(jī)制倒源,對于一些突發(fā)事件(用戶直接X瀏覽器、直接關(guān)機(jī)纪铺、發(fā)生地震……)相速,在其關(guān)閉瀏覽器的一段時(shí)間(session過期時(shí)間)后,其他用戶對網(wǎng)站的訪問鲜锚,會觸發(fā)session回收突诬,刪除過期記錄。所以芜繁,就不怕統(tǒng)計(jì)出來的數(shù)據(jù)不準(zhǔn)確了旺隙。
當(dāng)然了,就算沒人訪問骏令,你也可以在count時(shí)加上session_expire>time()來統(tǒng)計(jì)蔬捷。
但是也有誤差,就是session過期時(shí)間榔袋,5分鐘后過期周拐,就有5分鐘的誤差。時(shí)間設(shè)的越小凰兑,對數(shù)據(jù)庫的操作就越頻繁妥粟;越大,精確度就越低。
(3)這個(gè)session過期時(shí)間就意味著,如果用戶5分鐘內(nèi)不進(jìn)行任何操作缸濒,其就會自動退出登錄。所以播急,為了用戶體驗(yàn)好,請加上cookie自動登錄功能售睹。目前官網(wǎng)就是這么做的桩警。
(4)評論里有人提到,驗(yàn)證碼也會存入session昌妹,所以我們判斷的時(shí)候生真,就不能值統(tǒng)計(jì)有值的記錄了。
需要先獲取有值的數(shù)據(jù)捺宗,再判斷里面有沒有保存用戶信息的參數(shù)名柱蟀。雖然session_data字段是用二進(jìn)制存儲的,但是查詢出來就是一個(gè)字符串蚜厉。
比如长已,我們用的user下標(biāo)來保存的用戶信息,session('user',$data);//用戶登錄信息
//獲取真實(shí)會員數(shù)
//查詢有值的session記錄
$list=D('Session')->where(array('session_data'=>array('NEQ',''),'session_expire'=>array('lt',NOW_TIME)))->select();
//判斷值里是否有會員標(biāo)識
foreach($listas$k=>$value){
if(strpos($value['session_data'],'user')===false){
$count++;
}
dump($count);//真實(shí)會員人數(shù)
}
復(fù)制代碼
(5)由于每種瀏覽器都有各自的session機(jī)制,所以术瓮,如果一個(gè)人在一臺電腦上同時(shí)開了5種瀏覽器康聂,則數(shù)據(jù)庫會保存5條不同的記錄
實(shí)際使用時(shí),仍需要考慮搜索引擎的誤差胞四。在其抓取我們的頁面時(shí)恬汁,也會產(chǎn)生session。
五辜伟,最終結(jié)論
由于HTTP協(xié)議的限制:請求完成后就會斷開與客戶端的連接氓侧。所以實(shí)際上,我們根本無法精確而實(shí)時(shí)地統(tǒng)計(jì)在線人數(shù)
导狡!
盡管有各種各樣的方法來增加統(tǒng)計(jì)的精確度约巷,然而都是治標(biāo)不治本。
唯有放棄HTTP協(xié)議旱捧,使用“長連接”的鏈接方式独郎,才能精確判斷用戶在還是離
轉(zhuǎn)自 http://www.thinkphp.cn/topic/3217.html