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.两个跃点 跃点的含 ...
随机推荐
- (二)给IE6-IE9中的input添加HTML5新属性-placeholder
同样是最近遇到的一个小问题.因为IE9以下input是不支持placeholder属性的.在网上找到了解决方案,果断带走.正如鲁迅先生所说的‘拿来主义’:运用脑髓,放出眼光,自己来拿!感谢.借花献佛在 ...
- [Django1.6]The MEDIA_ROOT and STATIC_ROOT settings must different 解决
该项目有一个图片上传功能,为了把上传路径很简单,写在同一个静态文件路径,于wi7执行机器上没问题,今centos我们报道了机上,如下面的错误: django.core.exceptions.Impro ...
- HDU 4081-Parsing URL(水)
Parsing URL Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Others) Tota ...
- java volatile的一个验证反例(转)
网上关于java volatile的资料已经不少了,但搜了好久也没看到谁用代码很好地验证过使不使用volatile的差异.最近自己写了个测试,意外的看到了两者的明显区别,为什么说意外呢,因为根据我的测 ...
- 使用nodeitk进行对象识别
前言 东莞,晴,29至27度.忙了一天,最终能够写写东西了.今天继续昨天的话题,我们在昨天的例了基础上完好,通过匹配关键点求出映射从而找到场景中的已知对象. 目标 本文你将学习 採用nodeitk的f ...
- 怎样解决chm类型的文件在Windows操作系统中无法打开
又一次安装了Windows 7操作系统,发现chm文件类型Java API文档无法打开了,纠结了半天最终搞定,现总结一下: 1.选中该chm类型的文件,右键鼠标选择并点击"属性(R)&quo ...
- eclipse 配置android sdk和maven
首先下载 ADT-22.2.0.rar eclipse-jee-kepler-R-win32-x86_64.zip android SDK4.2.zip 分别解压在一个盘 将ADT里面的两个目录内容相 ...
- HDU 2159 FATE (完全背包+有限尚需时日)()双费背包
FATE Problem Description 近期xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务.久而久之xhd開始对杀怪产生的厌恶感,但又不得不通过杀怪来升 ...
- Hadoop 2.2.0 HA构造
在这篇文章中<Ubuntu和CentOS分布式配置Hadoop-2.2.0>介绍hadoop 2.2.0最主要的配置.hadoop 2.2.0中提供了HA的功能,本文在前文的基础上介绍ha ...
- What day is it
Description Today is Saturday, 17th Nov,2007. Now, if i tell you a date, can you tell me what day it ...