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.两个跃点 跃点的含 ...
随机推荐
- MVC5中使用SignalR2.0实现实时聊天室
原文 MVC5中使用SignalR2.0实现实时聊天室 有时候需要浏览器和服务端保持实时的通讯(比如在线聊天),SignalR的出现让这一切变得非常简单.它能够让服务端向客户端实时的推送消息.如果用户 ...
- 跟Bob大叔观OO原则
上篇总结了经典的23种 设计模式,详细的解读后期会陆续的详细揭开.使用设计模式的根本原因就是为了增强代码的复用性和可维护性.而面向对象是实现代码复用的有效途径,所以这里有必要了解一下OO的基本思想和原 ...
- js关于propotype的一些事-------Day62
近期在忙着搬家,忙忙活活的收拾这收拾那,原以为自己东西了了,谁知道东西是越收拾越多,各种崩溃啊..... 昨日在记录js动态生成表格的经典方式时,用到了createDocumentFragment() ...
- SQL Server 2012 复制(发布订阅的研究)
原文:SQL Server 2012 复制(发布订阅的研究) 已实现发布订阅功能,可以实现局域网内双击备份. 一.注意事项: a) 使用[事务复制]功能 b) 必须是相同的SqlServer 帐号和密 ...
- AngularJS 课程
AngularJS 教程(点我) AngularJS 通过新的属性和表达式扩展了 HTML. AngularJS 能够构建一个单一页面应用程序(SPAs:Single Page Application ...
- NSOJ Constructing Roads(图论)
There are N villages, which are numbered from 1 to N, and you should build some roads such that ever ...
- Layout Renderers
Layout Renderers NLog package ${activityid} - Puts into log a System.Diagnostics trace correlation i ...
- Git联系oschina托管代码版本号
工作一般使用SVN,近期好像GitHub有些火.看到开源中国上也有Git的开源版本号管理. 另外看到一篇文章说Git 比 SVN 要好. 就想多了解一下Git.顺便也能够把自己平时的一些代码保存在云端 ...
- asp.net 百度编辑器 UEditor 上传图片 图片上传配置 编辑器配置 网络连接错误,请检查配置后重试
1.配置ueditor/editor_config.js文件,将 //图片上传配置区 ,imageUrl:URL+"net/imageUp.ashx" //图片上传提交地址 ,im ...
- jdk1.7 变更
个人实遇: 1.6与1.7 :SimpleDateFormat,1.6要求yyyy必须小写,1.7兼容大小写: jdk 8 新特性: 官方pdf文档(英文版)私人下载地址:http://pan.bai ...