来源:键盘敲击者cncxz http://olu.desktopit.net/whitepaper.aspx
一般来说,用户离开系统的方式有四种:主动注销、会话超时、直接关闭浏览器、在当前窗口导航到其他页面,对于前两种(正常退出),我们很容易便可将该用户从在线列表中清除;而后两种(非正常退出),由于无法捕获退出事件的精确时间,只能等到会话超时后才能将该用户清除出在线列表。假设我们设置会话超时时间为60分钟,则必须在1小时后才能将非正常退出的用户从在线列表中清除,而在实际应用中,有相当一大批的用户都习惯于非正常退出,所以采用普通方式维护的在线用户列表的时效性可想而知~~
针对这种情况,2007年十一的时候做了一套解决方案,现在把它整理了一下发布出来,感觉可行的拿去研究一下吧。
在线用户模块(OnlineUserBlock)的简要介绍
主要功能特点
即时有效地提供一份在线用户列表
本模块是将这种尴尬降至最低的一个尝试。
在线用户包含会员、访客两种
实际应用中,站点应该还有许多访客,严格来说他们也算用户的一种(匿名用户),因而本模块的在线用户包含会员(普通用户)、访客(匿名用户)两种。
控制会员帐号不能在多处同时登陆
很多web系统(尤其是收费系统)非常厌恶多人共享同一帐号,本模块设置了一个控制开关,开启后会员帐号将禁止在多处同时登陆(要是俩人一个白天一个晚上分时段共享也是没有办法的),先登陆的将被后登陆的挤掉。
在线用户模块(OnlineUserBlock)解决方案的实现思路
- 核心功能的实现
- 在线用户列表时效性改进方案
为在线用户实体增设一个RefreshTime属性,在web系统的标志性页面(也可以是所有页面)上添加一个实现了System.Web.UI.ICallbackEventHandler接口的web控件, 该控件自页面加载成功开始利用javascript脚本无刷新循环调用(周期可配置)一个将当前用户的RefreshTime属性设置为当前时间的服务器端方法, 如果用户离开了所有包含此自动刷新控件的页面,那么RefreshTime属性便不会自动更新了。与此同时,我们在服务器端设置一个计时器,循环判断(周期可配置)在线列表中所有用户的RefreshTime属性, 自动清除那些超过特定时间(可配置,比客户端自动刷新周期长,远小于会话超时时间)不更新RefreshTime属性的用户。 当然,频繁的刷新会增大系统服务器负担,所以我们应该在服务器负担&在线用户列表时效性之间取一个均衡点,设置上文提到的三个参数。
- 将匿名用户(访客)加入在线名单
在ASP.NET2.0设置架构中新增加了一个anonymousIdentification元素(具体请参考msdn),设置其enabled 属性值为true可为当前应用程序启用匿名标识,之后调用HttpContext.Current.Request.AnonymousID可获取一个Guid转换而来的字符串, 使用这个字符串作为唯一标识创建普通用户(会员)&匿名用户(访客)的通用实体,在线用户列表即是对这一通用实体集合的维护。
- 禁止会员同时在多处登陆
这个功能的实现主要用两种途径:①某帐号一旦成功登陆后,在退出前不允许再次登陆;②登陆时判断此前是否有人用当前帐号登陆,若有则将其踢出。 对于第一种途径,由于非正常退出现象的存在,将有可能造成用户在某一特定时间内无法正常登陆,因而此处采用第二种途径。
- 在线用户列表时效性改进方案
- 在线列表的维护
-
下图(图一)描述了如何处理对 ASP.NET 资源的请求。首先,IIS 接收到请求,并将请求调度给 aspnet_isapi.dll;接下来,ASP.NET 引擎对已配置的 HTTP 模块进行初始化;最后将调用正确的 HTTP 处理程序,并呈现被请求的资源,将所生成的标记返回给 IIS 和请求客户端。
图一:IIS 和 ASP.NET 正在处理请求鉴于上述逻辑,在线用户列表维护的核心功能将在一个自定义的Http模块中实现。具体来说,就是在其PostAuthenticateRequest事件中判断当前请求是否由自动刷新控件(上文已交代其存在原因)发出, 若是则不作任何处理;否则,将按当前用户标识(HttpContext.Current.Request.AnonymousID)判断当前在线用户实体是否已创建,并根据判断结果做如下处理(如图二所示):针对这个HttpModule还有以下三点补充:
图二- 若当前用户未登陆(判断HttpContext.Current.User.Identity.IsAuthenticated),则自动创建匿名用户实体并加入在线名单,否则继续下一步;
- 检测在线用户列表中是否有他人采用当前帐号登陆,无则按照当前标识&登陆用户标识创建普通用户(会员)并加入在线名单,若有则继续下一步;
- 判断是否禁止会员同时在多处登陆(EnableMemberLoginSingleton),若未禁止则直接创建普通用户并加入在线名单,若已设置禁止则继续下一步;
- 踢出使用当前帐号登陆的其他人,创建普通用户并加入在线名单。
- 若当前在线用户实体为匿名用户(访客)且当前用户未登陆(判断HttpContext.Current.User.Identity.IsAuthenticated),或者当前在线用户实体为普通用户(会员)且当前用户已登陆,则直接更新当前在线用户的活动时间(ActiveTime)&自动刷新时间(RefreshTime),否则继续下一步;
- 转换当前在线用户实体(是匿名用户则更改为普通用户,是普通用户则更改为匿名用户)。
- 登陆时,创建当前在线用户(会员)实体,并将其加入在线列表(若有必要,须先踢出使用用此帐号登陆的其他人)
- 会员注销时,应将当前用户从在线列表中移除。
- 创建一个利用脚本无刷新更新当前在线用户自动刷新时间(RefreshTime)的web控件,并将其放置在web系统的主要页面上。
-
下图(图一)描述了如何处理对 ASP.NET 资源的请求。首先,IIS 接收到请求,并将请求调度给 aspnet_isapi.dll;接下来,ASP.NET 引擎对已配置的 HTTP 模块进行初始化;最后将调用正确的 HTTP 处理程序,并呈现被请求的资源,将所生成的标记返回给 IIS 和请求客户端。
在线用户的调用
在具体的web应用中可以自由调用当前在线用户实体,并且能够按条件(全部用户、全部访客、全部会员)获取全部的在线用户集合。
技术文档
- oluUserInfo:在线用户信息
- oluUserInfoProvider:在线用户信息提供者基类(使用本模块必须实现此类)
- oluMonitor:在线用户监控器
- oluHttpModule:在线用户模块的自定义Http模块
- AutoRefreshControl:自动循环刷新控件
详细的描述就不写了,可以从 这里下载NDoc 生成的类库文档。