现在的位置: 首页 > 综合 > 正文

Discuz!NT 在线用户功能简介

2012年04月15日 ⁄ 综合 ⁄ 共 9613字 ⁄ 字号 评论关闭

    声明:本文内容纯属个人观点,官方保留最终解释

     在上文(Discuz!NT URL地址重写) 中, 聊到了“在线用户”功能,因为当时介绍的重点不是“在线”
那一块,所以没做深入介绍。这就为今天这篇文章埋下了“伏笔”。因为在线这个功能太重要了,大家不妨
用VS打开我们产品的最新源码,然后搜索一下“OnlineUsers.”这个内容就会看到它在产品中被使用的“频
率”。
 
     好了,言归正传,下面就开始接着上一篇文章中所说的“OnlineUsers.ResetOnlineList();” 方法介绍
一下用户在线功能。

     首先请大家打开Discuz.Forum这个项目,从中找到"OnlineUsers.cs"这个文件,打开它找到
"ResetOnlineList()"这个函数,它的代码如下:

   

 1  /// <summary>
 2  /// 复位在线表, 如果系统未重启, 仅是应用程序重新启动, 则不会重新创建
 3  /// </summary>
 4  /// <returns></returns>

 5  public static int ResetOnlineList()
 6  {
 7   try
 8   {
 9    // 取得在线表最后一条记录的tickcount字段 (因为本功能不要求特别精确)
10                  //int tickcount = DatabaseProvider.GetInstance().GetLastTickCount();
11    // 如果距离现在系统运行时间小于10分钟
12    if (System.Environment.TickCount < 600000)
13    {
14     return InitOnlineList();
15    }

16    return -1;
17   }

18   catch
19   {
20    try
21    {
22     return InitOnlineList();
23    }

24    catch
25    {
26     return -1;
27    }

28   }

29
30  }

31
32

      这个函数本身就是在系统启动之后的10分钟内运行InitOnlineList函数,而为什么是10分钟(
不是别的时间段呢?),主要是因为这个值是个“估计值”,因为在线功能是系统的核心功能之一,
换句话说,系统在启动10分钟内,只要是用户在前台进行操作,绝对会用到这个核心功能,而这个
功能本身是依赖于数据库中的“dnt_onlines"表的(如果大家想了解这个表的结构,可以下载我们
产品的“数据字典”,里面有这方面内容的介绍)。所以创建(复位)和初始化这个数据表的责任就
交给了ResetOnlineList()这个函数(因为它是在HttpModule.cs中被绑定的,可以看作是系统运行
的起点,详情见上文)。

     好了,即然清楚了这个函数的作用,不妨再了解一下代码中InitOnlineList()方法,它的作用
就是运行下面这个SQL语句:

 

 1//该函数位于Discuz.Data.SqlServer项目中的UserManage.cs文件中
 2 public int CreateOnlineTable() 
 3        {
 4            try
 5            {
 6                StringBuilder sb = new StringBuilder();
 7                sb.Append("IF EXISTS (SELECT * FROM SYSOBJECTS WHERE id = object_id(N'[dnt_online]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) DROP TABLE [dnt_online];");
 8                sb.Append("CREATE TABLE [dnt_online] ([olid] [int] IDENTITY (1, 1) NOT NULL,[userid] [int] NOT NULL,[ip] [varchar] (15) NOT NULL,[username] [nvarchar] (20) NOT NULL,[nickname] [nvarchar] (20) NOT NULL,[password] [char] (32) NOT NULL,[groupid] [smallint] NOT NULL,[olimg] [varchar] (80) NOT NULL,[adminid] [smallint] NOT NULL,[invisible] [smallint] NOT NULL,[action] [smallint] NOT NULL,[lastactivity] [smallint] NOT NULL,[lastposttime] [datetime] NOT NULL,[lastpostpmtime] [datetime] NOT NULL,[lastsearchtime] [datetime] NOT NULL,[lastupdatetime] [datetime] NOT NULL,[forumid] [int] NOT NULL,[forumname] [nvarchar] (50) NOT NULL,[titleid] [int] NOT NULL,[title] [nvarchar] (80) NOT NULL,[verifycode] [varchar] (10) NOT NULL ) ON [PRIMARY];");
 9                sb.Append("ALTER TABLE [dnt_online] WITH NOCHECK ADD CONSTRAINT [PK_dnt_online] PRIMARY KEY CLUSTERED ([olid]) ON [PRIMARY]; ");
10                sb.Append("ALTER TABLE [dnt_online] ADD CONSTRAINT [DF_dnt_online_userid] DEFAULT ((-1)) FOR [userid],CONSTRAINT [DF_dnt_online_ip] DEFAULT ('0.0.0.0') FOR [ip],CONSTRAINT [DF_dnt_online_username] DEFAULT ('') FOR [username],CONSTRAINT [DF_dnt_online_nickname] DEFAULT ('') FOR [nickname],CONSTRAINT [DF_dnt_online_password] DEFAULT ('') FOR [password],CONSTRAINT [DF_dnt_online_groupid] DEFAULT (0) FOR [groupid],CONSTRAINT [DF_dnt_online_olimg] DEFAULT ('') FOR [olimg],CONSTRAINT [DF_dnt_online_adminid] DEFAULT (0) FOR [adminid],CONSTRAINT [DF_dnt_online_invisible] DEFAULT (0) FOR [invisible],CONSTRAINT [DF_dnt_online_action] DEFAULT (0) FOR [action],CONSTRAINT [DF_dnt_online_lastactivity] DEFAULT (0) FOR [lastactivity],CONSTRAINT [DF_dnt_online_lastposttime] DEFAULT ('1900-1-1 00:00:00') FOR [lastposttime],CONSTRAINT [DF_dnt_online_lastpostpmtime] DEFAULT ('1900-1-1 00:00:00') FOR [lastpostpmtime],CONSTRAINT [DF_dnt_online_lastsearchtime] DEFAULT ('1900-1-1 00:00:00') FOR [lastsearchtime],CONST  RAINT [DF_dnt_online_lastupdatetime] DEFAULT (getdate()) FOR [lastupdatetime],CONSTRAINT [DF_dnt_online_forumid] DEFAULT (0) FOR [forumid],CONSTRAINT [DF_dnt_online_forumname] DEFAULT ('') FOR [forumname],CONSTRAINT [DF_dnt_online_titleid] DEFAULT (0) FOR [titleid],CONSTRAINT [DF_dnt_online_title] DEFAULT ('') FOR [title],CONSTRAINT [DF_dnt_online_verifycode] DEFAULT ('') FOR [verifycode];");
11                sb.Append("CREATE INDEX [forum] ON [dnt_online]([userid], [forumid], [invisible]) ON [PRIMARY];");
12                sb.Append("CREATE INDEX [invisible] ON [dnt_online]([userid], [invisible]) ON [PRIMARY];");
13                sb.Append("CREATE INDEX [forumid] ON [dnt_online]([forumid]) ON [PRIMARY];");
14                sb.Append("CREATE INDEX [password] ON [dnt_online]([userid], [password]) ON [PRIMARY];");
15                sb.Append("CREATE INDEX [ip] ON [dnt_online]([userid], [ip]) ON [PRIMARY];");
16
17                return DbHelper.ExecuteNonQuery(CommandType.Text, sb.Replace("dnt_", BaseConfigs.GetBaseConfig().Tableprefix).ToString());
18            }

19            catch
20            {
21                return -1;
22            }

23        }

24
25

     该方法如果正常运行的话,会在数据库中建立dnt_onlines"这个表。用于记录在线用户的全部
信息。

     上文介绍的仅仅是系统“初始化”时所要做的“活”。而更重要的内容是用户从一访问论坛并
执行一系列操作(如登陆,发贴,浏览版块等)时,“在线用户”机制在里面所启的重要作用。

     这里先以登陆(系统)这一操作发生时,程序所做出的响应为例,看一下在线这块是如何进行绑
定的,请看如下代码(位于discuz.web项目的“aspx/1/”文件夹下的login.aspx.cs文件):
     
    

1
2    OnlineUsers.UpdateAction(olid, UserAction.Login.ActionID, 0, config.Onlinetimeout);
3    
4

    它的作用就是将已成功登陆系统的用户所执行的当前动作及相关信息更新到"dnt_onlines" 表
中。而这个函数本身所用到的四个参数要重点介绍一下:

     olid: 在线列表id,对应"dnt_onlines"表中的olid字段

     action:动作(结构类型,详情参见discuz.forum项目下的forumutils.cs文件)
           用于传递动作的相关信息如:ActionName动作名称和ActionDescription动作描述等)

     inid: 所在位置代码,即当前用户所访问的主题(TopicId)。因为登陆不牵扯主题操作,所
           以上面的值为0

     timeout: 无动作离线时间(config.Onlinetimeout),这个数据是在后台进行设置的,见下图:

 

      timeout的作用就是当有别人访问论坛时,在更新自身在线状态信息同时,用这个设置数据(
整型)与在线表(dnt_onlines)中的"lastupdatetime"字段(最后活动时间)进行比较,找出超过
timeout规定的时间的用户,将其在线状态(onlinestate)设置为0(即为离线)。而做这件事的函
数就是在项目“discuz.data.sqlserver”下的“UserManage.cs”中的“AddOnlineUser”函数,这
里将它的代码贴出来,详情见注释:

        

 1/// <summary>
 2        /// 执行在线用户向表及缓存中添加的操作。
 3        /// </summary>
 4        /// <param name="__onlineuserinfo">在组用户信息内容</param>
 5        /// <returns>添加成功则返回刚刚添加的olid,失败则返回0</returns>

 6        public int AddOnlineUser(OnlineUserInfo __onlineuserinfo, int timeout)
 7        {
 8
 9            string strDelTimeOutSql = "";
10            // 此处的设置见后台forum_uisetting.aspx.cs源码
11            // 如果timeout为负数则代表不需要精确更新用户是否在线的状态
12            if (timeout > 0)
13            {
14                if (__onlineuserinfo.Userid > 0)
15                {
16                    strDelTimeOutSql = string.Format("{0}UPDATE [{1}users] SET [onlinestate]=1 WHERE [uid]={2};", strDelTimeOutSql, BaseConfigs.GetTablePrefix, __onlineuserinfo.Userid.ToString());
17                }

18            }

19            else
20            {
21                timeout = timeout * -1;
22            }

23
24            if (timeout > 9999)
25            {
26                timeout = 9999;
27            }

28
29            System.Text.StringBuilder sb = new System.Text.StringBuilder();
30            System.Text.StringBuilder sb2 = new System.Text.StringBuilder();
31
32            IDataReader dr = DbHelper.ExecuteReader(CommandType.Text, string.Format("SELECT [userid] FROM [{0}online] WHERE [lastupdatetime]<'{1}'", BaseConfigs.GetTablePrefix, DateTime.Parse(DateTime.Now.AddMinutes(timeout * -1).ToString("yyyy-MM-dd HH:mm:ss"))));
33            try
34            {
35                while (dr.Read())
36                {
37                    sb.Append(",");
38                    sb.Append(dr[0].ToString());
39                    if (dr[0].ToString() != "-1")
40                    {
41                        sb2.Append(",");
42                        sb2.Append(dr[0].ToString());
43                    }

44                }

45            }

46            finally
47            {
48                dr.Close();
49            }

50
51            if (sb.Length > 0)
52            {
53                sb.Remove(01);
54                strDelTimeOutSql = string.Format("{0}DELETE FROM [{1}online] WHERE [userid] IN ({2});", strDelTimeOutSql, BaseConfigs.GetTablePrefix, sb.ToString());
55            }

56            if (sb2.Length > 0)
57            {
58                sb2.Remove(01);
59                strDelTimeOutSql = string.Format("{0}UPDATE [{1}users] SET [onlinestate]=0,[lastactivity]=GETDATE() WHERE [uid] IN ({2});", strDelTimeOutSql, BaseConfigs.GetTablePrefix, sb2.ToString());
60            }

61
62
63            DbParameter[] prams = {};
64            int olid = Utils.StrToInt(DbHelper.ExecuteScalar(CommandType.Text, strDelTimeOutSql + "INSERT INTO [" + BaseConfigs.GetTablePrefix + "online] ([userid],[ip],[username],[nickname],[password],[groupid],[olimg],[adminid],[invisible],[action],[lastactivity],[lastposttime],[lastpostpmtime],[lastsearchtime],[lastupdatetime],[forumid],[forumname],[titleid],[title], [verifycode])VALUES(@userid,@ip,@username,@nickname,@password,@groupid,@olimg,@adminid,@invisible,@action,@lastactivity,@lastposttime,@lastpostpmtime,@lastsearchtime,@lastupdatetime,@forumid,@forumname,@titleid,@title,@verifycode);SELECT SCOPE_IDENTITY()", prams).ToString(), 0);
65
66            // 如果id值太大则重建在线表
67            if (olid > 2147483000)
68            {
69                CreateOnlineTable();
70                DbHelper.ExecuteNonQuery(CommandType.Text, strDelTimeOutSql + "INSERT INTO [" + BaseConfigs.GetTablePrefix + "online] ([userid],[ip],[username],[nickname],[password],[groupid],[olimg],[adminid],[invisible],[action],[lastactivity],[lastposttime],[lastpostpmtime],[lastsearchtime],[lastupdatetime],[forumid],[titleid],[verifycode])VALUES(@userid,@ip,@username,@nickname,@password,@groupid,@olimg,@adminid,@invisible,@action,@lastactivity,@lastposttime,@lastpostpmtime,@lastsearchtime,@lastupdatetime,@forumid,@forumname,@titleid,@title,@verifycode);SELECT SCOPE_IDENTITY()", prams);
71                return 1;
72            }

73
74
75       

抱歉!评论已关闭.