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.两个跃点 跃点的含 ...
随机推荐
- uva 592 Island of Logic (收索)
Island of Logic The Island of Logic has three kinds of inhabitants: divine beings that always tel ...
- ssh无密码登陆(转)
[0]写在前面 由于ssh 实现的是免密码登陆,大致步骤是: 0.1) client通过ssh登陆到server: 0.2) server检查家目录下的.ssh文件, 并发送公钥文件 authoriz ...
- HDU1068/POJ1466_Girls and Boys(二分图/最大独立集=N-最大匹配)
解题报告 http://blog.csdn.net/juncoder/article/details/38160591 题目传送门(POJ) 题目传送门(HDU) 题意: 求满足条件的最大集合:集合内 ...
- ubuntu 14.04 安装搜狗拼音输入法
原文:ubuntu 14.04 安装搜狗拼音输入法 ubuntu桌面系统下终于有了好用的拼音法-搜狗拼音输入法,欲在ubuntu 14.04下安装搜狗拼音输入法相当的简单. 先到搜狗拼音官网下载对应的 ...
- HDU 2159 FATE (完全背包+有限尚需时日)()双费背包
FATE Problem Description 近期xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务.久而久之xhd開始对杀怪产生的厌恶感,但又不得不通过杀怪来升 ...
- Spring环境配置
研究spring3的时候发现一个非常好用的特性:环境配置(spring2是否有此特性未知) 官方演示样例代码例如以下: <!-- app-config.xml --> <beans ...
- Java得到的一周的最后一天的一段时间内
Java得到的一周的最后一天的一段时间内 1.设计源代码 LastDayOfWeek.java: /** * @Title:LastDayOfWeek.java * @Package:com.you. ...
- C# 一个WCF简单实例
以订票为例简单应用wcf 新建一个wcf服务应用程序 在IService1.cs定义服务契约 复制代码 代码如下: namespace WcfDemo { // 注意: 如果更改此处的接口名称 &qu ...
- hdoj 1226 超级password 【隐图BFS】
称号:hdoj 1226 超级password 分析:这题属于隐式图搜索,状态不是非常明显,须要自己建立. 事实上搜索说白了就是暴力. 这个题目就是,首先对给出的能够组成的全部的数依次枚举.长度从小到 ...
- ToDictionary() and ToList()
ToDictionary() and ToList() 前言: 有两个简单好用的LINQ扩展方法 ToDictionary() 和ToList(),你可能知道或不知道,但是它的的确确可以简化查询转化为 ...