最近在考慮作個統計在線用戶的功能。之前也作過,用的一些比較簡單的方法,可是缺點也很明顯:精確統計和服務器、數據庫壓力之間要作出平衡。
因此想找一個既能精確統計又能不佔用太多服務器資源的方法。先說說一些日常的作法:html
這個方法很直接,在用戶表裏加一個字段update_time,每次用戶進行操做,都更新這個字段爲當前時間,通常是在一個被全部Action繼承的基類裏寫這個操做。
而後定義一個過時時間,好比10分鐘,表示10分鐘沒進行任何操做的用戶默認爲不在線。這樣,統計當前在線用戶的sql語句大概是這樣web
select count(*) from think_user where update_time>now()-10*60
優勢:實現簡單,通俗易懂
缺點:1,對「在線」的定義模糊,萬一用戶看一篇文章時間比較長,10分鐘內沒進行任何操做,他就被忽略了;2,若是user表數據量很大,那效率將極差ajax
對於方法一的改進。新建一張表think_inline,字段有user_id、update_time,每次用戶操做時,先判斷表裏有沒有該用戶的記錄,沒有則新增,有就更新update_time。
並同時加上刪除失效數據操做sql
delete from think_inline where update_time<now()-10*60
這樣,統計在線就能夠直接count這張表就好了。並且這表的數據量不會很大(至少要比用戶表小的多)
優勢:減小數據庫壓力
缺點:仍然對「在線」的定義不許確數據庫
這個方法是綜合了一和二。新建一張表think_inline,也是在基類中定義每次用戶操做時更新時間,參考二的作法。
不一樣之處是,在每一個html模板裏,加上一個js定時器,setInterval('updateTime', 10*3600);每隔10分鐘發送一次ajax請求,更新update_time字段。這樣,即便用戶在一個頁面停留時間過長,也不會被誤認爲不在線了。而且能夠經過減小請求的間隔,來增長精確度,固然了,對服務器的壓力就更大了。
優勢:對在線的判斷較爲準確
缺點:仍然不能既精確又不增長服務器壓力,必須在二者之間進行取捨。瀏覽器
這也就是網上有人說的session存入數據庫的方法,這種方法優勢不少。服務器
session是存儲在服務器的一組臨時數據。通常狀況下,咱們在作用戶登陸時,會將用戶數據存入session。這樣,在任何頁面均可以方便調用,並且每一個客戶端會產生惟一的session_id,不會混餚。而且在關閉瀏覽器後,服務器會有session回收機制,自動刪除過時session。
這是session的優勢:惟一性、方便調用、不會過多佔用資源。可是也有缺點:在客戶端是以cookie方式保存的,禁用cookie就沒用了。
那麼,服務器是如何存放session的呢?他是默認將session以文件的方式保存在硬盤上的。但是,對於咱們碼農來講,操做數據庫要比讀文件方便的多,而且能夠對session數據進行各類操做。
而統計在線用戶人數就是經過統計有多少條session記錄來實現的。cookie
TP的SessionDb驅動就實現了這個功能。原理就是經過改寫PHP默認的session操做來實現,核心函數session_set_save_handler(),有興趣的能夠研究一下。該驅動將session的增、讀、取、和刪都放入了數據庫。
使用方法也很簡單:1,建表,驅動的註釋裏的sql語句運行下就好
2,添加配置:session
//Session配置 'SESSION_TYPE' => 'db', //數據庫存儲session 'SESSION_TABLE' => 'think_session', //存session的表 'SESSION_EXPIRE' => 600, //session過時時間
這樣,只要咱們在程序裏使用了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('該用戶不在線。') }
碼字太麻煩啦,先寫這麼多,後面總結該方法的幾大優勢以及注意事項。
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協議,使用「長鏈接」的連接方式,才能精確判斷用戶在仍是離
至此,完結
後續,官網將推出聊天室的功能,相似於webQQ,更加方便你們的交流。元芳,你怎麼看?