文章目录
1. 方案设计
使用 redis列表 存储两个用户之间的聊天数据
- 存储 内容使用
json
字符串封装,字段包括:fromId、toId、msg、time
使用 redis hash 存储一个用户未读的消息条数
- 存在问题:原子性问题 ??
2. 需求分析
2.1 连接数据库
Connect();
2.2 断开数据库
disConnect();
2.3 向 redis 输入命令
// _context:连接到数据库的对象
// redisCommand:hiredis 库函数,用于向数据库输入命令
setCommand(const string & data){
...
redisCommand(_context, data.c_str());
...
}
2.4 用户信息:存储格式
- 实现:reids 哈希表
2.5 注册:添加用户信息
addUserInfo(){
// 如果不存在,则创建
if( !HEXISTS(userInfo,wujie) )
hset(userInfo,wujie,123)
else
error
}
2.6 登陆:获取用户信息
searchUserInfo{
hget(userInfo,wujie);
}
2.7 注销:删除用户信息
delUserInfo()
hdel(userInfo,wujie)
}
2.8 聊天记录:存储格式
- 实现方式:链表
- 链表名:双方名字(eg: “wujie-chenshuai”),方便缩小遍历范围(只要找到表名就可以一次遍历出与该人的聊天记录)
- 内容:json 字符串(方便解析,无需再手写封装、解析字符串函数)
{
"time":"2018.01.25",
"fromId":"wujie",
"toId":"chenshuai",
"msg":"zaima?"
}
2.9 聊天记录:保存
addChatRecord(int num){
setCommand("multi"); // 开启事务
// 向事务中添加要执行的命令
for(int i = 0;i < num,i++)
{
ret = RPUSH("wujie-chenshuai","{"time":"2018.01.25","fromId":"wujie","toId":"chenshuai","msg":"zaima?"}");
assert(ret == QUEUED) // 输入命令可能有错误
}
ret = setCommand("exec"); // 执行事务
assert(ret == "OK") // 可能出现运行异常
}
2.10 聊天记录:获取
getChatRecord(int num){
LRANGE("wujie-chenshuai",0,num);
}
3. python 代码实例 – 参考
import json
import time
import redis
pool = redis.ConnectionPool(host=‘xxxx‘,port=6379, decode_responses=True)
conn = redis.Redis(connection_pool=pool)
""" function : fromid 用户给 toid 用户发送 msg 消息 Parameters: fromid: int类型,发送消息的用户id toid: int类型,接收消息的用户id msg:str类型,消息内容 return: bool类型,消息是否发送成功 """
def msgsend(fromid,toid,msg):
try:
timesocre = time.time() // 时间
// 封装聊天记录
dict = { "fromid":fromid,"toid":toid,"msg" :msg,"time":timesocre}
// 键名 - 双方名字
key = keyname(fromid, toid)
// 查看对方是否在线
aliveflag = checkuseralive(toid)
// 不在线 - 设置为未读消息数
if not aliveflag:
setwithoutnum(toid, key)
// 保存到 以双方名字 命名的列表
conn.lpush(key, json.dumps(dict))
return True
except:
return False
""" function: toid 用户读取 fromid 用户发送过来的 msg Parameters: fromid: int类型,发送消息的用户id toid: int类型,接收消息的用户id return: list,int( 消息列表 与 toid用户未读取fromid用户发送过来的消息条数) """
def msgread(fromid,toid):
key = keyname(fromid,toid)
msglen = conn.llen(key) // 查看双方聊天记录
msglist = conn.lrange(key,0,msglen) // 获取所有聊天记录
withoutmsgnum = returnwithoutnum(toid,key) // 获取未读消息数
return msglist,withoutmsgnum
""" function : 检查 userid 用户是否在线 Parameters: userid: int类型,消息的用户id return: bool类型,在线为True,不在线为 False """
def checkuseralive(userid):
# 检查用户在线,预留
return True
""" function: 设置 userid用户与另一个用户未读取消息的条数 Parameters: userid: int类型,哈希表 名字 key:str类型 用户名 return: bool类型,在线为True,不在线为False """
def setwithoutnum(userid,key):
// 如果用户在线
if conn.hexists(str(userid), key):
// 获取当前用户消息数
msgnum = conn.hget(str(userid), key)
// 当前用户未读消息数 + 1
conn.hset(str(userid), key, int(msgnum)+1)
else:
// 不存在,则新建哈希对
conn.hset(str(userid), key, 1)
""" function: 返回 userid用户 与 另一个用户的未读消息条数 Parameters: userid: int类型,哈希表名 key:str类型,用户名 return: 未读消息条数 """
def returnwithoutnum(userid,key):
// 若果有未读消息
if conn.hexists(str(userid), key):
// 获取(读出) 未读消息数
msgnum = conn.hget(str(userid), key)
// 清空未读消息数
conn.hset(str(userid), key,0)
return int(msgnum)
return 0
""" function: 根据 两个id 唯一生成 一个key(做表名,标识是双方的聊天记录) Parameters: fromid: int toid: int return: str """
def keyname(fromid,toid):
// eg: 不管 fromId = wujie & chengshuai 最终都生成 "chengshuai-wujie" 这个表名
key = (str(fromid)+"-"+str(toid) if (fromid > toid) else str(toid)+"-"+str(fromid))
return key
user1 = 23
user2 = 43
user3 = 212
user4 = 65
#用户1给用户2发送"你好"
msgsend(user1,user2,"你好")
#用户2读取用户1发送的消息
#第一个返回值返回全部聊天记录,第二个参数返回未读消息数量
msglist,withoutmsgnum = msgread(user1,user2)
print(msglist,withoutmsgnum)
#读取一条内容示例
print(json.loads(msglist[0]))
print(json.loads(msglist[1]))
print(json.loads(msglist[2]))