需求如题(估计平均在线人数几百,最大在线人数3000),
初步想法如下,欢迎提提意见:
public class UserOnLine
{
public static void pageLoad(System.Web.UI.Page page)
{
int minutes = 5;//定义5分钟页面不回传用户下线
DataTable dt = null;
if(page.Application["UserOnLine"] != null)
{
dt = (DataTable)page.Application["UserOnLine"];
}
else
{
dt = new DataTable();
dt.Columns.Add("UserName");
dt.Columns.Add("FirstLoadTime");
dt.Columns.Add("LastLoadTime");
}
//将当前用户添加到在线用户列表
string userName = page.Session["username"].ToString();
DataRow[] rows = dt.Select("UserName='" + userName + "'");
if(rows.Length>0)
rows[0][2] = System.DateTime.Now.ToString();
else
dt.Rows.Add(new object[]{userName,System.DateTime.Now.ToString(),System.DateTime.Now.ToString()});
dt.AcceptChanges();
//筛选长时间没有PostBack的用户,认为已经下线
DataRow[] downRows = dt.Select("Convert(LastLoadTime,'System.DateTime')<Convert('"+System.DateTime.Now.AddMinutes(-minutes).ToString()+"','System.DateTime')");
for(int i=0;i<downRows.Length;i++)
{
//将downRows[i]的记录保存到数据库
downRows[i].Delete();
}
dt.AcceptChanges();
page.Application["UserOnLine"] = dt;
}
}
在每个页面的Page_Load的时候调用此方法;
由于我的项目所有的页面都是继承子一个basePage类,该类继承System.Web.UI.Page,所以实际上并不需要改动任何一个页面,而只需要改一下basePage类就可以了。
>>>在每个页面的Page_Load的时候调用此方法;
为什么要做成页面的基类呢?做成HttpModule或在global.asax里不更好么?
也许在global.asax做一个Timer周期性地检查该表更好些
这样会导致系统反应变慢,呵呵。
而且在每个页面里写的话,你会累死的,服务器端也会大大增加负荷。
在这里面写
登陆的时候启动固定的Session
当然,这个Session必须具有可识别性
protected void Session_Start(Object sender, EventArgs e)
{
调用方法1,在你的数据库中写入登入时间,然后调整用户状态至已登陆
}
protected void Session_End(Object sender, EventArgs e)
{
调用方法2,在你的数据库中写入离站时间,然后调整用户状态至已离开
}
是啊,我就是觉得这种方法不是很好,我正要否决这种方法,却提不出更好的方法,所以先发个贴子问问 -- 我不喜欢随便反对别人的东西...
我自己试了一下global.asax,但是有几个问题没有解决好,所以希望有更详细的解决方法,尤其是思归老大,请指教
另外,其实所有页面继承basepage类这种方法其实我也觉得不好,表面看来好像很方便,但是页面上很多东西都通过代码生成,平白无故增加系统的负担,不知道大家怎么看
按照思归老大的提点,修改方案如下:
使用IHttpModule,加分讨论
首先创建实现IHttpModule接口的类MyModule:
using System;
using System.Web;
using System.Data;
namespace Test2004_5_13
{
public class MyModule : IHttpModule
{
public void Init(HttpApplication application)
{
application. AcquireRequestState += (new
EventHandler(this.Application_AcquireRequestState));
}
private void Application_AcquireRequestState (Object source,
EventArgs e)
{
HttpApplication mApplication = (HttpApplication)source;
HttpResponse Response=mApplication.Context.Response;
DataTable dt = null;
if(mApplication.Context.Application["UserOnLine"] != null)
{
dt = (DataTable)mApplication.Context.Application["UserOnLine"];
}
else
{
dt = new DataTable();
dt.Columns.Add("UserName");
dt.Columns.Add("FirstLoadTime");
dt.Columns.Add("LastLoadTime");
}
//将当前用户添加到在线用户列表
if(mApplication.Context.Session!=null)
{
if(mApplication.Context.Session["UserID"]!=null)
{
string userName = mApplication.Context.Session["UserID"].ToString();
DataRow[] rows = dt.Select("UserName='" + userName + "'");
if(rows.Length>0)
rows[0][2] = System.DateTime.Now.ToString();
else
dt.Rows.Add(new object[]{userName,System.DateTime.Now.ToString(),System.DateTime.Now.ToString()});
}
}
dt.AcceptChanges();
mApplication.Context.Application["UserOnLine"] = dt;
Response.Write("Beginning of Request" + dt.Rows.Count.ToString());
}
public void Dispose()
{
}
}
}
2)在web.config中注册
<httpModules>
<add type="Test2004_5_13.MyModule,Test2004_5_13" name="MyModule"/>
</httpModules>
那么每次页面PostBack的时候都会经过myModule;
现在有个新问题:
如何降低系统因此付出的代价?直接检索和更新数据库是否更不可取(估计要死人了...)?
这个是否跟写在global.asax.cs里是一样的??
protected void Application_AcquireRequestState(Object sender, EventArgs e)
{
HttpApplication mApplication = (HttpApplication)sender;
if(mApplication.Context.Session!=null)
{
if(mApplication.Context.Session["UserID"]!=null)
{
string userName = mApplication.Context.Session["UserID"].ToString();
}
}
}
至于离线检索,正如思归老大所说,在global.asax做一个Timer周期性地检查该表更好些-- 这个显然没有必要如此频繁的执行
Try This:
http://community.csdn.net/Expert/TopicView.asp?id=3605127
Application_AcquireRequestState
是什么事件,在什么时候引发??
global.asax做一个Timer
怎么做?
Count Your Online Visitors
http://www.aspnetresources.com/blog/count_your_visitors.aspx
Scheduled Execution in ASP.NET
http://weblogs.asp.net/ashben/archive/2003/10/11/31579.aspx
by the way, you should use "lock" to synchronize updating the Application variable this way
结贴了,以下提供参考:
看了一下yangzixp(扬子(四川·巴中),原理基本一致,不同的是你使用的FORMS身份验证,而且可以改进 -- 每次Application_AuthenticateRequest就检查并删除超时用户,肯定是太频繁了,改用Timer吧
总的来说,要做个在线人数统计简单,但是要做在线名单并且保存用户的访问日志,就需要耗费比较多的系统资源,是否划算就难说了(我只看需求文档,其他不管...);
前面用过的IHttpModule方法也不错,原先每用过,也学了一招...
感谢思归老大的帮忙,分就散了吧~
using System;
using System.ComponentModel;
using System.Web;
using System.Web.SessionState;
using System.Data;
using System.Data.OleDb;
namespace XsExam
{
/// <summary>
/// Global 的摘要说明。
/// </summary>
public class Global : System.Web.HttpApplication
{
private static System.Threading.Timer timer;
private const int interval = 1000 * 60 * 10;//检查在线用户的间隔时间
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
public Global()
{
InitializeComponent();
}
protected void Application_Start(Object sender, EventArgs e)
{
if (timer == null)
timer = new System.Threading.Timer(new System.Threading.TimerCallback(ScheduledWorkCallback),
sender, 0, interval);
DataTable userTable = new DataTable();
userTable.Columns.Add("UserID");//用户ID
userTable.Columns.Add("UserName");//用户姓名
userTable.Columns.Add("FirstRequestTime");//第一次请求的时间
userTable.Columns.Add("LastRequestTime");//最后一次请求的时间
userTable.Columns.Add("ClientIP");//
userTable.Columns.Add("ClientName");//
userTable.Columns.Add("ClientAgent");//
//userTable.Columns.Add("LastRequestPath");//最后访问的页面
userTable.PrimaryKey = new DataColumn[]{userTable.Columns[0]};
userTable.AcceptChanges();
Application.Lock();
Application["UserOnLine"] = userTable;
Application.UnLock();
}
protected void Session_Start(Object sender, EventArgs e)
{
}
protected void Application_BeginRequest(Object sender, EventArgs e)
{
}
protected void Application_EndRequest(Object sender, EventArgs e)
{
}
protected void Application_AcquireRequestState(Object sender, EventArgs e)
{
HttpApplication mApp = (HttpApplication)sender;
if(mApp.Context.Session == null) return;
if(mApp.Context.Session["UserID"]==null ) return;
string userID = mApp.Context.Session["UserID"].ToString();
DataTable userTable = (DataTable)Application["UserOnLine"];
DataRow curRow = userTable.Rows.Find(new object[]{userID});
if(curRow != null)
{
this.GetDataRowFromHttpApp(mApp,ref curRow);
}
else
{
DataRow newRow = userTable.NewRow();
this.GetDataRowFromHttpApp(mApp,ref newRow);
userTable.Rows.Add(newRow);
}
userTable.AcceptChanges();
Application.Lock();
Application["UserOnLine"] = userTable;
Application.UnLock();
}
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
}
protected void Application_Error(Object sender, EventArgs e)
{
}
protected void Session_End(Object sender, EventArgs e)
{
}
protected void Application_End(Object sender, EventArgs e)
{
}
#region Web 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
}
#endregion
private void GetDataRowFromHttpApp(HttpApplication mApp,ref DataRow mRow)
{
if(mApp.Context.Session == null) return;
if(mApp.Context.Session["UserID"]==null || mApp.Context.Session["UserName"]==null) return;
string userID = mApp.Context.Session["UserID"].ToString();
string userName = mApp.Context.Session["UserName"].ToString();
//string requestPath = mApp.Request.Path;
if(mRow["UserID"].ToString().Length<1)
{
mRow["UserID"] = userID;
mRow["UserName"] = userName;
mRow["FirstRequestTime"] = System.DateTime.Now;
mRow["ClientIP"] = mApp.Context.Request.UserHostAddress;
mRow["ClientName"] = mApp.Context.Request.UserHostName;
mRow["ClientAgent"] = mApp.Context.Request.UserAgent;
}
mRow["LastRequestTime"] = System.DateTime.Now;
//mRow["LastRequestPath"] = requestPath;
}
private void ScheduledWorkCallback (object sender)
{
string filter = "Convert(LastRequestTime,'System.DateTime') < Convert('" + System.DateTime.Now.AddSeconds(-interval/1000).ToString() + "','System.DateTime')";
DataTable userTable = (DataTable)Application["UserOnLine"];
DataRow[] lineOutUsers = userTable.Select(filter);
for(int i=0;i<lineOutUsers.Length;i++)
{
DataRow curRow = lineOutUsers[i];
//保存到数据库
XsStudio.Database db = new XsStudio.Database();
curRow.Delete();
}
userTable.AcceptChanges();
Application.Lock();
Application["UserOnLine"] = userTable;
Application.UnLock();
}
}
}