Discuz教程网

浅析DzX 1.5与DzX2中在线时间不更新的问题与其修复

[复制链接]
authicon dly 发表于 2011-9-3 20:23:34 | 显示全部楼层 |阅读模式
在Discuz!X 1.5中,康盛恢复了在线时间这一功能,但有不少站长报告说会员的在线时间并不见更新。当然,一如既往的,官方没有承认此错误,同时还有一位官方人员对DzX的在线时间机制做了个解析(传送门在此(一楼))。那么实际上是不是真的没有bug呢?必然不可能没有拉,如果真没有那谈何修复,就更没有这篇文章了。。。

分析在康盛官方人员的那篇帖子中我们也可以看到,更新用户的在线时间是在updatesession()这个函数(位于source/function/function_core.php)中实现的。下面我来分析下其中关于在线时间的代码(注释为蝦米所加,所参考版本为DzX2 110629,SVN ID:function_core.php 22982 2011-06-13 01:52:33Z zhangguosheng )
  1. if($_G['uid'] && TIMESTAMP - $_G['member']['lastactivity'] > 21600) {//如果用户已经登陆,并且当前时间与数据库中记录的用户上次活动时间之差大于6小时
  2. if($oltimespan && TIMESTAMP - $_G['member']['lastactivity'] > 43200) { //如果在线时间功能开启,并且当前时间与用户上次活动时间之差大于12小时
  3. $total = DB::result_first("SELECT total FROM ".DB::table('common_onlinetime')." WHERE uid='$_G[uid]'");
  4. DB::update('common_member_count', array('oltime' => round(intval($total) / 60)), "uid='$_G[uid]'", 1); //那么对在线时间进行更新
  5. }
  6. DB::update('common_member_status', array('lastip' => $_G['clientip'], 'lastactivity' => TIMESTAMP, 'lastvisit' => TIMESTAMP), "uid='$_G[uid]'", 1); //更新数据库中的用户上次活动时间
  7. }
复制代码
也许细心的站长已经发现了其中的问题,如果一个会员他很活跃,任意2次访问间隔时间均不超过12小时,岂不是他的在线时间再也不会更新了?
修复方法既然找到了问题,那修复就很简单了,将
  1. if($oltimespan && TIMESTAMP - $_G['member']['lastactivity'] > 43200) {
复制代码
一行替换为
  1. if($oltimespan) {
复制代码
保存即可。
其实又是一个很低级的逻辑上的错误,那么多报告bug的难道Discuz官方就不能进行下确认吗?这个问题并不难发现,我也是别人找了我说有这个问题想让我帮忙看下才发现的。吐槽的话已不想多说,就这样子吧。
作者:江湖大虾仁








上一篇:PHP 将bmp图片转为jpg等其他任意格式的图片
下一篇:2011中秋节祝福页面,让您的网站带来流量
authicon  楼主| dly 发表于 2011-9-3 20:28:09 | 显示全部楼层

解析Discuz! X1.5更新在线时间的机制

更新的机制:如果当前时间 - 该用户的上一次更新时间(如果上一次更新时间不存在那么就是该用户最后的活动时间)>后台设置的用户在线时间更新时长*60,那么就会往common_onlinetime表里更新一下该用户的总在线时间和当月在线时间。如果当前时间 - 该用户的最后活动时间 > 43200(12个小时),那么就从common_onlinemember表里查询该用户的总在线时间更新到common_member_count表里的在线时间字段中。后台有一个清空本月在线时间的计划任务,每个月1日0点执行一次,清空所有用户当月的在线时间。

具体流程:

在template/common/footer.htm文件中可以找到这么一处代码。
  1. <!--{eval updatesession();}-->
复制代码

这个函数的定义在source/function/function_core.php文件中,具体代码如下:

  1. function updatesession($force = false) {

  2. global $_G;
  3. static $updated = false;
  4. if(!$updated) {
  5. $discuz = & discuz_core::instance();
  6. $oltimespan = $_G['setting']['oltimespan'];
  7. $lastolupdate = $discuz->session->var['lastolupdate'];
  8. if($_G['uid'] && $oltimespan && TIMESTAMP - ($lastolupdate ? $lastolupdate : $_G['member']['lastactivity']) > $oltimespan * 60) {
  9. DB::query("UPDATE ".DB::table('common_onlinetime')."
  10. SET total=total+'$oltimespan', thismonth=thismonth+'$oltimespan', lastupdate='" . TIMESTAMP . "'
  11. WHERE uid='{$_G['uid']}'");
  12. if(!DB::affected_rows()) {
  13. DB::insert('common_onlinetime', array(
  14. 'uid' => $_G['uid'],
  15. 'thismonth' => $oltimespan,
  16. 'total' => $oltimespan,
  17. 'lastupdate' => TIMESTAMP,
  18. ));
  19. }
  20. $discuz->session->set('lastolupdate', TIMESTAMP);
  21. }
  22. foreach($discuz->session->var as $k => $v) {
  23. if(isset($_G['member'][$k]) && $k != 'lastactivity') {
  24. $discuz->session->set($k, $_G['member'][$k]);
  25. }
  26. }

  27. foreach($_G['action'] as $k => $v) {
  28. $discuz->session->set($k, $v);
  29. }

  30. $discuz->session->update();

  31. $updated = true;

  32. if($_G['uid'] && TIMESTAMP - $_G['member']['lastactivity'] > 21600) {
  33. if($oltimespan && TIMESTAMP - $_G['member']['lastactivity'] > 43200) {
  34. $total = DB::result_first("SELECT total FROM ".DB::table('common_onlinetime')." WHERE uid='$_G[uid]'");
  35. DB::update('common_member_count', array('oltime' => round(intval($total) / 60)), "uid='$_G[uid]'", 1);
  36. }
  37. DB::update('common_member_status', array('lastip' => $_G['clientip'], 'lastactivity' => TIMESTAMP, 'lastvisit' => TIMESTAMP), "uid='$_G[uid]'", 1);
  38. }
  39. }
  40. return $updated;
  41. }
复制代码

在updatesession()函数里进行了更新用户在线时间的操作,具体分析如下:
  1. $oltimespan = $_G['setting']['oltimespan'];
复制代码
$_G['setting']['oltimespan']就是后台定义的用户在线时间更新时长,单位为分钟,默认为10。具体设置的位置如图:
182239g1rhkg8mk16bp5b1.png.thumb.jpg


  1. $lastolupdate = $discuz->session->var['lastolupdate'];
复制代码

$discuz->session->var['lastolupdate']为该用户在session表记录的最后更新时间。

  1. if($_G['uid'] && $oltimespan && TIMESTAMP - ($lastolupdate ? $lastolupdate : $_G['member']['lastactivity']) > $oltimespan * 60) {
复制代码
这里重点讲下这个if判断里的TIMESTAMP - ($lastolupdate ? $lastolupdate : $_G['member']['lastactivity']) > $oltimespan * 60这个条件:
$_G['uid']:用户uid
TIMESTAMP: 当前服务器时间
$lastolupdate: 用户的上一次更新时间
$_G['member']['lastactivity']:用户最后的活动时间
$oltimespan:用户在线时间更新时长

($lastolupdate ? $lastolupdate : $_G['member']['lastactivity']) 这是一个三元表达式,如果存在$lastolupdate那么就是$lastolupdate,如果不存在那么就是$_G['member']['lastactivity']。

所以这个条件的意思就是:
如果当前时间 - 用户的上一次更新时间或者用户的最后活动时间 > 用户在线时间更新时长*60,那么就执行{}里的操作。

  1. DB::query("UPDATE ".DB::table('common_onlinetime')." SET total=total+'$oltimespan', thismonth=thismonth+'$oltimespan', lastupdate='" . TIMESTAMP . "' WHERE uid='{$_G['uid']}'");
复制代码
这句代码的意思是根据uid=$_G['uid']往common_onlinetime表里更新该用户的总在线时间total,当月在线时间thismonth,上一次更新时间lastupdate.

注意:这里每次更新都是在原来的基础上+用户在线时间更新时长。

  1. if(!DB::affected_rows()) {
  2. DB::insert('common_onlinetime', array(
  3. 'uid' => $_G['uid'],
  4. 'thismonth' => $oltimespan,
  5. 'total' => $oltimespan,
  6. 'lastupdate' => TIMESTAMP,
  7. ));
  8. }
复制代码
这句话的意思是如果上一条执行的语句没有返回影响的条数,那么就说明common_onlinetime这个表里还没有uid=$_G['uid']这条记录,那就往common_onlinetime表里新插入一条数据,其中的thismonth、total、lastupdate分别为当月在线时间、用户的总在线时间、上一次更新时间.

  1. $discuz->session->set('lastolupdate', TIMESTAMP);
复制代码
把session表里该用户的上一次更新时间字段更新为当前时间。

到这里往common_onlinetime表里更新在线时间的处理结束,下面看下是如何更新到用户表common_member_count表里的。

接着往下看代码,还是在updatesession()函数里,找到如下代码。

  1. if($_G['uid'] && TIMESTAMP - $_G['member']['lastactivity'] > 21600) {
  2. if($oltimespan && TIMESTAMP - $_G['member']['lastactivity'] > 43200) {
  3. $total = DB::result_first("SELECT total FROM ".DB::table('common_onlinetime')." WHERE uid='$_G[uid]'");
  4. DB::update('common_member_count', array('oltime' => round(intval($total) / 60)), "uid='$_G[uid]'", 1);
  5. }
  6. DB::update('common_member_status', array('lastip' => $_G['clientip'], 'lastactivity' => TIMESTAMP, 'lastvisit' => TIMESTAMP), "uid='$_G[uid]'", 1);
  7. }
复制代码
$_G['uid']:用户uidTIMESTAMP:当前服务器时间
$_G['member']['lastactivity']:用户的最后活动时间
$oltimespan:用户在线时间更新时长

if($_G['uid'] && TIMESTAMP - $_G['member']['lastactivity'] > 21600) {
首先判断是否存在uid,同时是否满足当前时间 - 用户的最后活动时间 > 21600(21600/3600=6个小时)这个条件,满足就执行{}里的操作。

if($oltimespan && TIMESTAMP - $_G['member']['lastactivity'] > 43200) {
判断是否存在$oltimespan,同时是否满足当前时间 - 用户的最后活动时间 > 43200(43200/3600=12个小时)这个条件,满足就执行下面的操作。

  1. $total = DB::result_first("SELECT total FROM ".DB::table('common_onlinetime')." WHERE uid='$_G[uid]'");DB::update('common_member_count', array('oltime' => round(intval($total) / 60)), "uid='$_G[uid]'", 1);
复制代码
首先查询common_onlinetime表里uid=$_G['uid']的用户的总在线时间,然后更新到common_member_count表里。

这里说明下:common_onlinetime表里记录的时间是以分钟为单位,common_member_count表里是以小时为单位。我们在前台页面看到的用户在线时间就是common_member_count表里的oltime字段的值,即总在线时间。


  1. DB::update('common_member_status', array('lastip' => $_G['clientip'], 'lastactivity' => TIMESTAMP, 'lastvisit' => TIMESTAMP), "uid='$_G[uid]'", 1);
复制代码
更新common_member_status表里uid=$_G['uid']的最后访问的IP、最后的活动时间、最后的活动时间。

由于每个页面都会加载footer.htm文件,同时又会加载updatesession()函数,这样每次访问都会判断用户的在线时间是否需要更新。
总的来说,在线时间的更新就是先更新到common_onlinetime表里,然后当满足当前时间-最后活动时间>12个小时的话再更新到common_member_count表中的在线时间。同时后台有一个清空本月在线时间的计划任务,每个月1日0点执行一次,清空所有用户当月的在线时间,更新用户的当月在线时间。
authicon 【天字第一号】 发表于 2012-8-15 17:16:48 | 显示全部楼层
看一下。。。。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1314学习网 ( 浙ICP备10214163号 )

GMT+8, 2025-5-2 09:01

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表