------------------程序做得再好,数据有问题照样是个死 !

关于聊天系统

在线聊天功能的总设计思路:


现在有两个浏览器在不同的两台电脑上面, 浏览器A登陆的是系统管理员,

浏览器B登陆的是总监, 现在系统管理员想给总监发送消息,而浏览器之间

是不可以相互之间直接发送消息的。因为一个浏览器是在A电脑上面,

一个浏览器是在B电脑上面。这两台电脑是不可以相互之间直接通讯的。

但是这两个浏览器都访问了CRM这个网站。我们就想一个办法,

系统管理员先把这个消息发给服务器,然后总监再向这个服务器要,

那么这样就涉及到一个问题。系统管理员把这个消息给服务器,服务器又怎么知道

这个消息是给总监的呢?所以必须给服务器一个实体,这个里面要包括哪几个东西呢?

首先谁发的必须要有,我们给这个实体取名为 ChatMsg , ChatMsg里面有这么几个属性

FormUserID(发送者ID),FormRealName(发送者名称),ToUserID(接收者ID),

ToRealName(接收者名称),MessageBody(内容),SendTime(发送时间)

系统管理员把这个消息实体发给这个网站以后,总监登录系统以后向这个服务器要,

总监的这个请求就必须带一个userid过来,服务器就会把userid等于这个值的消息给总监。

现在A电脑系统管理员和B电脑总监 都发送消息给这个网站,而这个网站还要做其他很多事情

这样就相当于它的职责不太清晰,压力也变大了。

那么想一个办法,CRM网站就只是负责处理登录,权限控制这些功能。那么这个聊天消息

的实时处理以及入库就交给另一个网站来处理。

就相当于一个应用服务器 ,那么在分布式结构里面,CRM就充当着

Web服务器。而这个应用服务器上面呢 说白了 也就相当于一个IIS,而在这个IIS上面发布了

一个站点,这个站点是用WCF构建的, 这个WCF服务 负责 聊天消息的实时转发和

历史消息的入库操作。那么这个时候 CRM的职责只需要 将浏览器发送过来的ChatMsg实体

转发给WCF聊天服务即可。无需理会这里面的逻辑了,这样CRM压力释放了,职责也明确了。

总监发送ID想从服务器拿到自己的消息,而CRM也只需要把ID转发给WCF聊天服务,由WCF处理

返回再由CRM转回给总监即可。这个时候CRM的职责就是一个转发的功能。所有的业务都不需要

处理,直接交给WCF服务处理,

那么再分析这些具体里面需要什么实际技术去实现呢?

发送消息需要什么就可以简单实现呢?在线聊天的界面,左边是一个用户列表,数据来源于

sysUserInfo这个表,右边上面用一个面板来做接收消息区域,放一个div就行了。

下面再放一个文本框,里面就是用来填要发送的消息内容。再有两个按钮,发送和查看历史消息

点击发送用Ajax的异步请求把文本框里的消息内容发送个CRM。

那么这个异步请求到底发送哪些消息过去?

FormUserID(发送者ID),FormRealName(发送者名称)表示发送者系统管理员,在服务器

已经登录就可以在Session中拿到,在这里没必要浏览器发送到CRM。就只需要把

ToUserID(接收者ID) ToRealName(接收者名称),MessageBody(内容),发送给服务器。

SendTime(发送时间)也不需要,就直接取服务器时间。

所以Ajax请求只需要把

ToUserID(接收者ID),ToRealName(接收者名称),MessageBody(内容),发送给CRM网站即可。

然后在CRM网站内部实例化ChatMsg对象就行了。

那么WCF这边级需要2个方法,发送消息需要一个方法。

SendMessage(ChatMsg msg)方法,这个方法

将来被CRM网站调用的。CRM实例化ChatMsg通过参数传给SendMessage方法。

因为发送消息以后级行了,不需要等待服务器处理完,所以这个方法是个数据报方法即可。

第二个方法:B电脑用户要从服务器拿到自己的消息,发送userid到CRM,然后转到WCF

将userid 传给方法 GetMessage(int userid),就把当前传过来的用户ID对应的聊天消息返回

,而返回的格式就直接返回ChatMsg实体的集合List<ChatMsg>,这个方法就必须是请求响应方法

,为什么?因为必须等服务器响应完才能返回,否则拿回去的数据会是空的。

那么这SendMessage方法接收到这个实体所做的逻辑步骤有哪些?

1,将msg消息实体先入Redis队列,

2,入库。加入到历史表中,方便将来查询。

GetMessage()方法呢?

获取消息逻辑有:

1,根据用户id从Redis中将消息出队 就直接返回

这两个方法的逻辑确定下来后,就要考虑第3件事。考虑性能问题

当用户频繁使用聊天功能进行发送消息的时候,会出现频繁的操作数据库

这样会导致数据库的压力非常大。

那该如何解决:

1,使用一个线程每隔1分钟将一批聊天记录进行一次性入库操作。

这样就能解决频繁操作数据库的问题。提升了db的性能,

但是引发了另一个问题,在进行一批数据的入库操作时,如果这批数据过大,

如何保证快速插入到db中。

解决方案:使用 system,data.SqlBluckCopy类来进行高效的批量插入处理,

这个时候注意:利用历史消息队列来统一存放要入库的消息实体。不然数据

从Redis中出队就没有了。

注意问题:

1,每个用户在Redis中都要有一个专属的消息队列(不然获取不到消息):

allmsg+msg.ToUserID,

这样就引发出一个设计问题:

1,在Redis中开启一个实时队列,此实时队列是每个用户都有一个的,

用来存储别人给这个用户发送的消息数据。

2,在Redis中开启一个历史消息队列,这个队列中存储的是每个用户的聊天消息,将来

每隔固定时间就将此消息队列中的数据入库(考虑到分表来存储)

3,使用单例类来管理上面两个队列利用Lock()锁防止多线程的并发问题。








posted @ 2015-03-14 23:30  俊落笔如歌  阅读(320)  评论(0编辑  收藏  举报
           人的本事不是与生俱来的,不是你掌握了多少,而是当你面对一个未知问题的时候,你能用多少时间来掌握!       ---------俊落笔如歌