在线用户实体缓存解决方案

随着网站访问量的增加,在线用户实体信息的存储方式变得重要起来。存储在线用户的信息一般有这三种方案:

1、用户的实体信息保存在Session里,简单方便,随着Session的过期用户信息自动过期。

2、用户信息保存在数据库中,用一个表存储在线的用户信息。

3、用户信息保存在内存。

当前项目用的是第一种方法,把用户的实体信息保存在Session中,虽然使用方便,但总感觉很别扭。Discuz!NT使用的是第二种方法,把在线用户标识保存在一个表中,从cookie跟读取用户的ID,并从用户信息表查询该用户的信息,组装到实体中。如果有大量的用户在线同时操作时,这也不是一个很好的解决办法。

这里选择第三种解决方案,把用户信息保存到内存。

我们使用Dictionary来存储用户信息,由于Dictionary不是线程安全的,因此需要注意只能单线程更新字典。

先定义一个保存用户信息的实体:


internal class UserEntity<T>
{
/// <summary>
/// 用户信息
/// </summary>
internal T UserInfo { get; set; }

/// <summary>
/// 添加到列表的时间戳
/// </summary>
internal DateTime Timestamp { get; set; }
}

使用泛型包装用户实体,并增加一个时间戳,表示该用户信息添加到内存的时间,过期是根据这个时间来判断的。

再增加一个在线用户信息管理类:


/// <summary>
/// 在线用户缓存管理
/// </summary>
/// <typeparam name="T"></typeparam>
public class UserCacheManager<T>
{
#region 静态属性
/// <summary>
/// 静态用户缓存表
/// </summary>
private static Dictionary<long, UserEntity<T>> _UserList = new Dictionary<long, UserEntity<T>>();
/// <summary>
/// 过期时间
/// </summary>
private static int _ExpiredMinutes = 30;
/// <summary>
/// 定时器
/// </summary>
private static Timer _Timer = null;
#endregion

#region 静态构造函数
/// <summary>
/// 静态构造函数
/// 初始化计时器
/// </summary>
static UserCacheManager()
{
_Timer = new Timer(new TimerCallback(TimerClear), null, 60000, _ExpiredMinutes * 60000);
}
#endregion

#region 私有方法
/// <summary>
/// 清除在线用户
/// </summary>
/// <param name="sender"></param>
private static void TimerClear(object sender)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncClear));
}

/// <summary>
/// 异步清除过期的在线用户
/// </summary>
/// <param name="sender"></param>
private static void AsyncClear(object sender)
{
//当前时间
DateTime timestamp = DateTime.Now.AddMinutes(0 - _ExpiredMinutes);
//过期的用户列表
var expiredUserList =
(from userEntity in _UserList where userEntity.Value.Timestamp <= timestamp select userEntity);
if (expiredUserList != null && expiredUserList.Count() > 0)
{
List<long> expiredUserIdentities = expiredUserList.Select(o => o.Key).ToList();
lock (_UserList)
{
foreach (long userId in expiredUserIdentities)
_UserList.Remove(userId);
}
}
}
#endregion

#region 公共方法
/// <summary>
/// 增加在线用户
/// </summary>
/// <param name="userIdentity">用户身份标识</param>
/// <param name="userInfo">用户实体</param>
public static void Add(long userIdentity, T userInfo)
{
lock (_UserList)
{
#region 创建用户实体
UserEntity<T> userEntity = new UserEntity<T>
{
Timestamp = DateTime.Now,
UserInfo = userInfo
};
#endregion

if (_UserList.Keys.Contains(userIdentity))
{
_UserList[userIdentity] = userEntity;
}
else
{
_UserList.Add(userIdentity, userEntity);
}
}
}

/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="userIdentity"></param>
/// <returns></returns>
public static T Get(long userIdentity)
{
lock (_UserList)
{
if (_UserList.Keys.Contains(userIdentity))
{
_UserList[userIdentity].Timestamp = DateTime.Now;
return _UserList[userIdentity].UserInfo;
}
else
{
return default(T);
}
}
}

/// <summary>
/// 移除用户缓存信息
/// </summary>
/// <param name="userIdentity"></param>
public static void Remove(long userIdentity)
{
if (_UserList.Keys.Contains(userIdentity))
{
lock (_UserList)
{
_UserList[userIdentity].Timestamp = DateTime.Now.AddDays(-1);
}
}
}
#endregion
}

Dictionary<long, UserEntity<T>> _UserList用来保存在线的用户列表。

在静态构造函数中声明了一个定时器,定时器负责清理过期的用户信息。并把清理用户信息的方法装入线程池执行。

MSDN:只要不修改Dictionary,Dictionary就可以同时支持多个阅读器。即便如此,从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。当出现枚举与写访问互相争用这种极少发生的情况时,必须在整个枚举过程中锁定集合。若允许多个线程对集合执行读写操作,您必须实现自己的同步。

所以在更新Dictionary中,都锁定了字典,防止多线程冲突。

源代码在经过富文本编辑器后显示有点问题,感兴趣的朋友可以 从这里下载源码

http://www.cnblogs.com/moozi/archive/2010/05/22/1741610.html

转载于:https://www.cnblogs.com/mlj322/archive/2010/06/04/1751545.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值