WebIM(2)---消息缓存
在一步一步打造WebIM(1)一文中,已经介绍了如何实现一个简单的WebIM,但是,这个WebIM有一个问题,就是每一次添加消息监听器时,都必须访问一次数据库去查询是否有消息,显然,如果用户比较多时,必然对数据库的压力比较大。解决这个问题的一个方法就是先将消息缓存在内存中,不立即写入数据库,等到缓存满了才写入数据库。本文将介绍如何实现消息缓存。
基本思路
实现一个消息缓存管理类,以用户为单位缓存所有消息,每一个用户对应着一个List<Message>,保存着该用户新收到的消息,消息缓存管理用一个Hashtable保存着所有用户对应的List<Message>。
具体实现代码如下:
public class MessageCacheManagement
{
static MessageCacheManagement m_Instance = new MessageCacheManagement(); static public MessageCacheManagement Instance
{
get { return m_Instance; }
} private MessageCacheManagement()
{
} Int32 m_Count = 0;
Hashtable m_Cache = new Hashtable(); List<Message> GetUserMessageCache(String user)
{
if (!m_Cache.ContainsKey(user))
{
m_Cache.Add(user, new List<Message>());
} return m_Cache[user] as List<Message>;
} /// <summary>
/// 清除缓存
/// </summary>
public void Clear()
{
lock (m_Cache)
{
List<Message> msgs = new List<Message>();
foreach (DictionaryEntry ent in m_Cache)
{
(ent.Value as List<Message>).Clear();
}
m_Count = 0;
}
} /// <summary>
/// 获取所有缓存的消息
/// </summary>
/// <returns></returns>
public List<Message> GetAll()
{
lock (m_Cache)
{
List<Message> msgs = new List<Message>();
foreach (DictionaryEntry ent in m_Cache)
{
foreach (Message msg in ent.Value as List<Message>)
{
msgs.Add(msg);
}
}
return msgs;
}
} /// <summary>
/// 获取某一用户缓存的消息的最小时间
/// </summary>
public Nullable<DateTime> GetMinCreatedTime(string user)
{
lock (m_Cache)
{
List<Message> userMsgs = GetUserMessageCache(user);
return userMsgs.Count == 0 ? null : new Nullable<DateTime>(userMsgs[0].CreatedTime);
}
} /// <summary>
/// 在缓存中插入一条消息
/// </summary>
/// <param name="user"></param>
/// <param name="msg"></param>
public void Insert(String user, Message msg)
{
List<Message> userMsgs = null; lock (m_Cache)
{
userMsgs = GetUserMessageCache(user);
} lock (userMsgs)
{
userMsgs.Add(msg);
m_Count++;
}
} /// <summary>
/// 查找缓存中接受者为user,发送时间大于from的消息
/// </summary>
public List<Message> Find(String user, DateTime from)
{
List<Message> userMsgs = null; lock (m_Cache)
{
userMsgs = GetUserMessageCache(user);
} lock (userMsgs)
{
List<Message> msgs = new List<Message>(); int i = 0;
while (i < userMsgs.Count && userMsgs[i].CreatedTime <= from) i++; while (i < userMsgs.Count) { msgs.Add(userMsgs[i]); i++; } return msgs;
}
} /// <summary>
/// 获取消息总量
/// </summary>
public Int32 Count
{
get { return m_Count; }
}
}
添加消息监听器
增加消息缓存后,添加消息监听器的流程也要修改,具体思路是先获取消息接收者在缓存中发送时间最早的消息的发送时间,显然,如果监听器的From大于或等于这个最小发送时间时,无需访问数据库,可以直接访问缓存。具体代码修改为:
/// <summary>
/// 添加消息监听器,如果查找到符合监听器条件的消息,返回false,此时不会添加监听器
/// 如果没有查找到符合监听器条件的消息,返回true,此时监听器将被添加到m_Listeners中
/// </summary>
public bool AddListener(String receiver, String sender, Nullable<DateTime> from, WebIM_AsyncResult asynResult)
{
MessageListener listener = new MessageListener(receiver, sender, from, asynResult);
lock (m_Lock)
{
if (!m_Listeners.ContainsKey(receiver))
{
m_Listeners.Add(receiver, new List<MessageListener>());
}
List<MessageListener> listeners = m_Listeners[receiver] as List<MessageListener>; //获取用户receiver缓存的消息的最小发送时间
Nullable<DateTime> min = MessageCacheManagement.Instance.GetMinCreatedTime(receiver); List<Message> messages = new List<Message>(); //当from >= 缓存在内存中的消息的最小时间时,不必查询数据库
if (min == null || from == null || from.Value < min.Value)
{
//查询数据库
messages.AddRange(Find(receiver, sender, from));
} //在缓存中查询
messages.AddRange(MessageCacheManagement.Instance.Find(receiver, from.Value)); if (messages.Count == 0)
{
//插入监听器
listeners.Add(listener);
}
else
{
//发送消息
listener.Send(messages);
}
return messages.Count == 0;
}
}
发送消息
增加消息缓存后,发送消息的流程也要修改,具体思路是:先将消息保存到缓存中,之后判断缓存的消息的总数,如果超过设定的上限,就将消息写入数据库。具体代码修改为(您可以通过修改MAX_CACHE_COUNT修改缓存消息数的上限):
/// <summary>
/// 插入新的消息,插入消息后将查询m_Listeners中是否有符合条件的监听器,如存在,同时将消息发送出去
/// </summary>
public Message NewMessage(String receiver, String sender, DateTime createdTime, String content)
{
lock (m_Lock)
{
Message message = new Message(sender, receiver, content, createdTime, ++m_MaxKey); List<Message> messages = new List<Message>();
messages.Add(message); if (m_Listeners.ContainsKey(receiver))
{
List<MessageListener> listeners = m_Listeners[receiver] as List<MessageListener>;
List<MessageListener> removeListeners = new List<MessageListener>();
foreach (MessageListener listener in listeners)
{
if ((listener.Sender == "*" || String.Compare(listener.Sender, sender, true) == 0) &&
(listener.From == null || message.CreatedTime > listener.From))
{
listener.Send(messages);
removeListeners.Add(listener); System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(listener.Complete));
}
} foreach (MessageListener listener in removeListeners)
{
//移除监听器
listeners.Remove(listener);
}
} MessageCacheManagement.Instance.Insert(receiver, message); if (MessageCacheManagement.Instance.Count >= MAX_CACHE_COUNT)
{//超过缓存的最大值,将缓存中的消息全部写入数据库
//启动事务
SQLiteTransaction trans = m_Conn.BeginTransaction(); try
{
List<Message> cacheMsgs = MessageCacheManagement.Instance.GetAll(); foreach (Message msg in cacheMsgs)
{SQLiteCommand cmd = new SQLiteCommand(
"insert into Message (Receiver,Sender,Content,CreatedTime,Key) values (?,?,?,?,?)",
m_Conn
);
cmd.Parameters.Add("Receiver", DbType.String).Value = msg.Receiver;
cmd.Parameters.Add("Sender", DbType.String).Value = msg.Sender;
cmd.Parameters.Add("Content", DbType.String).Value = msg.Content;
cmd.Parameters.Add("CreatedTime", DbType.DateTime).Value = msg.CreatedTime;
cmd.Parameters.Add("Key", DbType.Int64).Value = msg.Key; cmd.ExecuteNonQuery();
} trans.Commit();
}
catch
{
trans.Rollback();
} MessageCacheManagement.Instance.Clear();
} return message;
}
}
本文来自:http://www.cnblogs.com/lucc/archive/2010/04/27/1722470.html
WebIM(2)---消息缓存的更多相关文章
- nordic mesh中的消息缓存实现
nordic mesh中的消息缓存实现 代码文件msg_cache.h.msg_cache.c. 接口定义 头文件中定义了四个接口,供mesh协议栈调用,四个接口如下所示,接口的实现代码在msg_ca ...
- 6张图为你分析Kafka Producer 消息缓存模型
摘要:发送消息的时候, 当Broker挂掉了,消息体还能写入到消息缓存中吗? 本文分享自华为云社区<图解Kafka Producer 消息缓存模型>,作者:石臻臻的杂货铺. 在阅读本文之前 ...
- [Storm] 内部消息缓存
这篇文件翻译自 http://www.michael-noll.com/blog/2013/06/21/understanding-storm-internal-message-buffers/ 当进 ...
- WebIM(3)----性能测试
WebIM系列文章 在一步一步打造WebIM(1)和(2)中,已经讨论了如何开发一个WebIM,并且使用缓存来提高WebIM的性能,本文将编写一个程序模拟大量用户登录来对WebIM进行性能测试. 1. ...
- Netty构建分布式消息队列实现原理浅析
在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子, ...
- C#分布式消息队列 EQueue 2.0 发布啦
前言 最近花了我几个月的业余时间,对EQueue做了一个重大的改造,消息持久化采用本地写文件的方式.到现在为止,总算完成了,所以第一时间写文章分享给大家这段时间我所积累的一些成果. EQueue开源地 ...
- Net分布式系统之四:RabbitMQ消息队列应用
消息通信组件Net分布式系统的核心中间件之一,应用与系统高并发,各个组件之间解耦的依赖的场景.本框架采用消息队列中间件主要应用于两方面:一是解决部分高并发的业务处理:二是通过消息队列传输系统日志.目前 ...
- 消息队列与RabbitMQ
1 什么是消息队列 消息指进程或应用间通信的数据:队列是保存数据的结构:消息队列是指进程或应用间通信时,保存消息的容器.消息队列独特的机制和结构保证了消息发送者和接收者之间良好的异步通信. 2 为什么 ...
- AMQ学习笔记 - 06. 可靠消息传送
概述 本文介绍JMS中可能发生消息故障的3个隐患阶段,以及确保消息安全的3种保障机制. 故障分析 在介绍可靠传送的确保机制之前,先分析消息在传送的过程中可能在哪个阶段出现问题. 1.两个跃点 跃点的含 ...
随机推荐
- 如何安装一个优秀的BUG管理平台(转)
前言 就BUG管理而言,国内的禅道做得很不错,而且持续有更新.我们来看看如何从头到尾安装禅道,各位要注意的是,不是文章深或者浅,而是文章如何在遇到问题的时候,从什么途径和用什么方法解决问题的.现在发觉 ...
- crm创建报告补充导航
报告导航实现动态交互体验报告. 通过使用各种类型的操作的,报告允许用户导航到特定的报告.Microsoft Dynamics CRM 记录或其它网站 动态钻取到 Microsoft Dynamics ...
- Java多线程总结之由synchronized说开去(转)
这几天不断添加新内容,给个大概的提纲吧,方面朋友们阅读,各部分是用分割线隔开了的: synchronized与wait()/notify() JMM与synchronized ThreadLocal与 ...
- 启示—地点IT高管20在职场心脏经(读书笔记6)
启示--一个IT高管20在职场心脏经 第七章 关于销售 用"最"来形容公司的销售.能够用上若干的词汇: 最牛,最累,最精,最傻,最有钱,最贱,最能吹.最能装... 1.1 销售 ...
- 文件类似的推理 -- 超级本征值(super feature)
基于内容的变长分块(CDC)技术,能够用来对文件进行变长分块.而后用来进行反复性检測,广泛用于去重系统中.后来又出现了对相似数据块进行delta压缩,进一步节省存储开销. 所以就须要一种高效 ...
- SQL_sql的简单查询
***********************************************声明*************************************************** ...
- JavaScript中的各种奇葩问题
原文:JavaScript中的各种奇葩问题 JavaScript浮点数 var a = (0.1 + 0.2) + 0.3; var b = 0.1 + (0.2 + 0.3); console.lo ...
- RSA加密解密(PHP Demo)
$private_key = '-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDpoODVtnSztGyb//p+g/Ob36jb3jzWzS2qovOj ...
- ubuntu 14.04 hadoop eclipse 0配置基本环境
动人的hadoop第二天.构造hadoop该环境还花了两天时间,在这里写自己配置的过程,我希望能帮助! 我将文中用到的全部资源都分享到了 这里,点开就能下载,不须要一个个的找啦! 当中有<Ha ...
- 面向对象三大特征之多态——Java笔记(七)
多态: 同一个实体同时具有多种形式 编译时的类型有声明该变量时使用的类型决定,运行时的类型有实际赋值给变量的对象决定 如果编译时类型和运行时类型不同,就出现多态 例: clas ...