zoukankan      html  css  js  c++  java
  • 关于聊天系统

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


    现在有两个浏览器在不同的两台电脑上面, 浏览器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()锁防止多线程的并发问题。








    人的本事不是与生俱来的,不是你掌握了多少,而是当你面对一个未知问题的时候,你能用多少时间来掌握!
  • 相关阅读:
    serialVersionUID作用
    为什么要使用SLF4J而不是Log4J
    认识Log4j
    Java解析xml文件四种方式
    数据结构之R进制转换
    栈的压入、弹出序列
    中间件学习之RMI+JDBC远端数据库的访问
    Linux程序设计综合训练之简易Web服务器
    Html5笔记之小结
    PhoneGap + Dreamweaver 5.5 无法在模拟器中打开的问题
  • 原文地址:https://www.cnblogs.com/dianshen520/p/4338569.html
Copyright © 2011-2022 走看看