WebIM系列文章

一步一步打造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)---消息缓存的更多相关文章

  1. nordic mesh中的消息缓存实现

    nordic mesh中的消息缓存实现 代码文件msg_cache.h.msg_cache.c. 接口定义 头文件中定义了四个接口,供mesh协议栈调用,四个接口如下所示,接口的实现代码在msg_ca ...

  2. 6张图为你分析Kafka Producer 消息缓存模型

    摘要:发送消息的时候, 当Broker挂掉了,消息体还能写入到消息缓存中吗? 本文分享自华为云社区<图解Kafka Producer 消息缓存模型>,作者:石臻臻的杂货铺. 在阅读本文之前 ...

  3. [Storm] 内部消息缓存

    这篇文件翻译自 http://www.michael-noll.com/blog/2013/06/21/understanding-storm-internal-message-buffers/ 当进 ...

  4. WebIM(3)----性能测试

    WebIM系列文章 在一步一步打造WebIM(1)和(2)中,已经讨论了如何开发一个WebIM,并且使用缓存来提高WebIM的性能,本文将编写一个程序模拟大量用户登录来对WebIM进行性能测试. 1. ...

  5. Netty构建分布式消息队列实现原理浅析

    在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子, ...

  6. C#分布式消息队列 EQueue 2.0 发布啦

    前言 最近花了我几个月的业余时间,对EQueue做了一个重大的改造,消息持久化采用本地写文件的方式.到现在为止,总算完成了,所以第一时间写文章分享给大家这段时间我所积累的一些成果. EQueue开源地 ...

  7. Net分布式系统之四:RabbitMQ消息队列应用

    消息通信组件Net分布式系统的核心中间件之一,应用与系统高并发,各个组件之间解耦的依赖的场景.本框架采用消息队列中间件主要应用于两方面:一是解决部分高并发的业务处理:二是通过消息队列传输系统日志.目前 ...

  8. 消息队列与RabbitMQ

    1 什么是消息队列 消息指进程或应用间通信的数据:队列是保存数据的结构:消息队列是指进程或应用间通信时,保存消息的容器.消息队列独特的机制和结构保证了消息发送者和接收者之间良好的异步通信. 2 为什么 ...

  9. AMQ学习笔记 - 06. 可靠消息传送

    概述 本文介绍JMS中可能发生消息故障的3个隐患阶段,以及确保消息安全的3种保障机制. 故障分析 在介绍可靠传送的确保机制之前,先分析消息在传送的过程中可能在哪个阶段出现问题. 1.两个跃点 跃点的含 ...

随机推荐

  1. [ACM] ZOJ 3816 Generalized Palindromic Number (DFS,暴力枚举)

    Generalized Palindromic Number Time Limit: 2 Seconds      Memory Limit: 65536 KB A number that will ...

  2. Hadoop0.20.2 Bloom filter应用演示样例

    1. 简单介绍 參见<Hadoop in Action>P102 以及 <Hadoop实战(第2版)>(陆嘉恒)P69 2. 案例 网上大部分的说明不过依照<Hadoop ...

  3. 设计模式入门之装饰器模式Decorator

    //装饰模式定义:动态地给一个对象加入一些额外的职责. //就添加功能来说.装饰模式比生成子类更为灵活 //这也提现了面向对象设计中的一条基本原则,即:尽量使用对象组合,而不是对象继承 //Compo ...

  4. cocos2dx 3.x Value、Vector和Map意识

    1. Value cocos2d::Value 这包括一个非常大的数字原生类型(int,float,double,bool,unsigned char,char* 和 std::string)外 加s ...

  5. C++11于once_flag,call_once分析的实现

    基于该分析llvm的libc++,代替gun的libstdc++,由于libstdc++的代码里太多宏了,看起来蛋疼. 在多线程编程中,有一个常见的情景是某个任务仅仅须要运行一次.在C++11中提供了 ...

  6. 离robots.txt启动网络爬虫之旅

    要成为一个网络爬虫或搜索引擎(在这里,共同蜘蛛)它不会陌生,在搜索引擎爬虫的第一个文件或者访问该网站上浏览robots.txt该.robots.txt文件讲述了蜘蛛server哪些文件要观看正在. 当 ...

  7. 開始开发 Dashboard Widgets,第2章,读书笔记

    文件夹:http://blog.csdn.net/wide288/article/details/40298693 主要内容: widgets 的组成是什么. 怎么创建 info.plist 文件 怎 ...

  8. OpenGL于MFC使用汇总(三)——离屏渲染

    有时直接创建OpenGL形式不适合,或者干脆不同意然后创建一个表单,正如我现在这个项目,创建窗体不显示,它仅限于主框架.而我只是ActiveX里做一些相关工作,那仅仅能用到OpenGL的离屏渲染技术了 ...

  9. 《Javascript权威指南》学习笔记之十八:BOM新成就(1)--client存储数据(Web SQL DataBase实现)

    使用本地存储和会话存储能够实现简单的对象持久化,能够对简单的键值对或对象进行存储.可是,对于比較复杂的关系数据进行处理时,就要用Web SQL Database.浏览器对Web SQL Databas ...

  10. 初步swift该研究指出语言(基本数据类型)

    笔者:fengsh998 原文地址:http://blog.csdn.net/fengsh998/article/details/28258805 转载请注明出处 假设认为文章对你有所帮助,请通过留言 ...