一、聊天场景分析
聊天无非是要实现以下功能:
(1)选择用户列表中的用户;
(2)选择用户后,实现发送信息;
(3)发送消息后的读取状态;
二、数据库设计
鉴于一的分析,现在只需要设计3个数据表:
1. 简单介绍
(1)好友列表;用于保存发送者的好友
(2)聊天信息数据表;用于保存聊天的信息。用户发送的信息都保存到此表。
(3)用户在线表;用于记录用户是否在线。用户只要登录进系统,就把用户的信息增加进在线表,用户离线则删除。
对于用户在线表,由于频繁涉及增加删除的操作,可能会让人觉得是否比较消耗系统的资源,其实我觉得,如果只在用户基本的信息表增加一个字段,用于记录用户是否在线,用户进入系统,则修改此字段的值为true,用户离开则修改此字段的值为false。
虽然此种做法也可以,但是效率也未必比增加删除这个操作来的高。因为用户进入系统和离开系统都需要找到该用户所在的行,并修改该字段的值;而使用在线表,需要在用户进入系统则把用户信息增加进在线表,属于写入操作,用户离开则删除该记录,属于删除操作。我觉得虽然写入操作可能比修改要耗时,因为写入需要大量的对磁盘执行写入操作,但是删除只需要删除磁盘中该标志,效率并不会比修改来的低,所以本文使用‘’用户在线表‘’来判断用户是否在线。当然在用户表增加字段来记录用户是否在线的做法也行,只不过本文选择了后者。
2. 详细介绍
2.1 好友列表
好友列表需要的字段如下:(以下设计的表多了一个标识用户身份的字段,主要针对有的应用存在多种用户身份,对于单用户身份的应用,可删除身份字段)
字段 | 字段描述 |
my_id | String,我的id |
my_status | String,我的身份 |
friend_id | String,好友id,用来标识每一个好友 |
frind_status | String,好友身份 |
备注说明,好友列表中有三个字段,my_id为用户的id,friend_id为我的朋友的id,friend_status(可选值),每次添加一个好友,都往好友列表中新增行即可,随后前台遍历,只需要根据登录用户的id就可以查找到他的所有好友,查找到的好友push进一个数组,然后再根据数组的好友id进行遍历获取每一个好友的基本信息,即可实现列表填充。
新浪的聊天模块的图:
2.2 聊天信息表
聊天信息表需要的字段包括以下:(以下设计的表多了一个标识用户身份的字段,主要针对有的应用存在多种用户身份,对于单用户身份的应用,可删除身份字段)
字段 | 字段描述 |
id | String,唯一值,用来标识每一条聊天信息 |
from_uid | String,发送者id,用来标识发送者 |
from_status | String,用来标识发送者的身份 |
to_uid | String,接收者id,用来标识接收者 |
to_status | String,用来标识接收者的身份 |
content | String,聊天内容,用于保存聊天信息 |
time | lontInt,用于保存信息发送的时间毫秒 |
flag | Boolean,用于保存信息的读取标志(判断是否已读) |
备注说明:
(1)上面为啥没有涉及到用户的昵称与头像之类的信息,那是因为用户的昵称与头像不可保存到此表中,如果保存到此表,当用户修改昵称与头像时,那么相同的两个用户之间的聊天的头像与昵称展示就会不一样。所以用户的头像与昵称需要进行额外连表(用户基本信息表)查询,进而再展示,这样可以做到相同的两个用户聊天,两个用户的聊天信息所涉及到的头像与昵称永远是当前用户的头像与昵称。
(2)其实不止聊天场景需要这样,评论的场景更需要这样,因为我之前写过一篇评论场景的分析,没有考虑到用户的评论所涉及的头像与昵称需要连表(用户的基本信息表)查询,而是直接作为字段保存下来了,所以我设计出来的东西是存在bug的,因为我懒,所以就不改了。
2.3 用户在线表
此表设计可能存在着多余的感觉,因为只在用户基本信息表增加一个字段用来记录用户是否在线也可以实现判断用户是否在线。但是本着数据表各司其职,责任分离的想法,所以设计了用户在线表。(同上,多了一个标识用户身份的字段)
字段 | 字段描述 |
id | String,用来标识每一位在线用户 |
online_id | String,用来标识在线用户的id |
online_status | String,用来标识在线用户的身份 |
socket_id | String,用来保存在线用户的socket标识符 |
2.4 好友列表与聊天表与在线表的协作完成聊天的功能
(1)用户进入系统并且进入聊天界面的时候,则向在线表追加用户记录并建立socket_id,保存到用户在线表中。
(2)用户进入聊天模块后,选择用户好友列表,开始进行聊天操作;选中用户好友列表中的任意一个好友作为接收者,需要立即查询接收者是否在线。
接收者若在线,则直接将信息内容通过socket顺着网线把信息内容传输过去给接收者。之后需要将信息存入聊天表,此时该信息的flag为true,表示已读。
接收者若不在线,则直接把信息保存到数据库中,此时该信息的flag为false,表示未读。
注意点:
1.关于用户的在线与离线:
用户进入系统并且进入聊天模块时,需要向在线表追加记录,表示用户在线。
用户退出聊天模块,但是未退出系统时,此时仍然将该用户的记录从在线表删除,表示用户离开聊天模块,此时当其他用户发信息过来时,通通设置为未读。
2.关于即时聊天:
由于是即使聊天,需要判断接收者是否在线。如果接收者在线,需要立即把信息内容顺着网线给它推送过去。
所以当发送者进入聊天模块,选择接收者时,需要每隔n秒向服务器发送一次请求,判断接收者是否在线,防止接收者突然登录,而让即时聊天的功能没有实现。
而这个n秒的如何来界定,这个学问就大了,如果每60秒发送一次请求查询接收者是否在线,那么肯定会存在接收者在这60秒内突然登录的情况,所以,按道理来说,要考虑发送短信的最快速度,例如用户输入一个字符,立即点击enter进行发送短信,这个过程耗时最少是多少,那么就每次间隔这个时间查询一次接收者是否在线,我假设它是1~2秒。但是我考率的是每隔1~2秒发送一次请求,是否会让服务器的压力过大呢?这个我还不清楚。所以这让我很好奇微信和qq是怎样设置这个用户在线判断的时间间隔的?