方案設計
使用redis列表存儲兩個用戶之間的聊天數據,存儲內容使用json字符串封裝,字段包括:fromid、toid、msg、time四個字段。
使用redis hash存儲一個用戶未讀的消息條數。
存在問題:原子性問題。
Python Demo實現
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: setwaithoutnum(toid, key) conn.lpush(key, json.dumps(dict)) return True except: return False """ function:toid用戶讀取fromid用戶發送過來消息 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類型,消息的用戶id key:str類型 return:bool類型,在線為True,不在線為False """ def setwaithoutnum(userid,key): if conn.hexists(str(userid), key): msgnum = conn.hget(str(userid), key) conn.hset(str(userid), key, int(msgnum)+1) else: conn.hset(str(userid), key, 1) """ function:返回userid用戶與另一個用戶的未讀消息條數 Parameters: userid:int類型,消息的用戶id 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): 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) # msgsend(user2,user1,"。。。") # msglist,withoutmsgnum = msgread(user2,user1) # print(msglist,withoutmsgnum) #讀取最新一條內容示例 print(json.loads(msglist[0])) print(json.loads(msglist[1])) print(json.loads(msglist[2])) # print(json.loads(msglist[3])) # print(json.loads(msglist[5])) # print(json.loads(msglist[6])) # print(json.loads(msglist[7]))
Redis配合lua
上一個版本沒有考慮到原子性的問題,我這里采用lua腳本了,減少網絡io的同時,保證了整個執行過程的原子性。
import json import time import redis pool = redis.ConnectionPool(host='xxx',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): lua1 = """ local flag = tostring(ARGV[1]) if(flag == "False") then if(redis.call("hexists",KEYS[1],KEYS[2]) == 1) then local msgnum = tonumber(redis.call("hget",KEYS[1],KEYS[2])) redis.call("hset",KEYS[1],KEYS[2],msgnum+1) else redis.call("hset",KEYS[1],KEYS[2],1) end end redis.call("lpush", KEYS[2],ARGV[2]) """ timesocre = time.time() dict = {"fromid":fromid,"toid":toid,"msg" :msg,"time":timesocre} key = keyname(fromid, toid) aliveflag = checkuseralive(toid) script2 = conn.register_script(lua1) script2(keys=[str(toid),key],args=[str(aliveflag),json.dumps(dict)]) """ function:toid用戶讀取fromid用戶發送過來消息 Parameters: fromid:int類型,發送消息的用戶id toid:int類型,接收消息的用戶id return:list(消息總長度,未讀消息數量,未讀消息內容) """ def msgread(fromid,toid): lua2 = """ local result = {} local msglistlen = tonumber(redis.call("llen",KEYS[2])) table.insert(result, msglistlen); if(redis.call("hexists",KEYS[1],KEYS[2]) == 1) then local msgnum = redis.call("hget",KEYS[1],KEYS[2]) redis.call("hset",KEYS[1],KEYS[2],0) table.insert(result, msgnum); table.insert(result,redis.call("lrange",KEYS[2],0,-msgnum)) return result end table.insert(result, 0); table.insert(result, "") return result """ key = keyname(fromid, toid) script2 = conn.register_script(lua2) ldata = script2(keys=[str(toid), key]) return ldata[0],ldata[1],ldata[2] """ function:檢查userid用戶是否在線 Parameters: userid:int類型,消息的用戶id return:bool類型,在線為True,不在線為False """ def checkuseralive(userid): # 檢查用戶在線,預留 return False """ function:根據兩個id唯一生成一個key Parameters: fromid: int toid:int return: str """ def keyname(fromid,toid): 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,"2324242") msgsend(user1,user2,"ewewe") msgsend(user1,user2,"ewe") # msgsend(user1,user2,"22") # msgsend(user1,user2,"233") # msgsend(user2,user1,"3232") #讀取測試 #用戶2讀取用戶1發送的消息 #第一個返回值返回全部聊天記錄,第二個參數返回 msglistlen,withoutmsgnum,msglist = msgread(user1,user2) print(msglistlen,withoutmsgnum,json.loads(msglist[0])) #用戶1讀取用戶2發送的消息 #第一個返回值返回全部聊天記錄,第二個參數返回 # msglist,withoutmsgnum = msgread(user2,user1) # print(msglist,withoutmsgnum)