实现获取聊天消息的功能
聊天消息保存在Redis里名为“chat_list”列表中,新的消息在列表右侧,老的消息在列表左侧。每次返回最右侧的20条信息。获取聊天消息的功能对应的方法为get_chat_list()。修改这个方法,实现获取消息列表:
01 def get_chat_list(self): 02 """ 03 你需要实现这个方法 04 05 获取聊天消息列表。 06 使用Redis的列表实现。Key为self.chat_list属性中保存的字符串,可以直接使用。 07 获取列表右端20条信息,但不要删除。 08 09 需要注意,从Redis中获取的数据一个列表,列表里面是bytes型的字符串,所以需要 10 先把这个列表展开,把里面的bytes型的字符串解密为普通字符串以后再用json解析为 11 字典。接下来讲解析出来的字典放入一个新的列表中。最后返回新的列表。 12 :return: 包含字典的列表 13 """ 14 chat_list = self.client.lrange(self.chat_list, -20, -1) 15 chat_info_list = [] 16 for chat in chat_list: 17 chat_info = json.loads(chat.decode()) 18 chat_info_list.append(chat_info) 19 return chat_info_list
其中,主要说明如下:
- 第14行:使用列表的lrange命令获取但不删除列表中的信息。-20表示从右往左数第20条信息,-1表示最右边的信息
- 第16-18行:由于lrange返回的数据是包含bytes型数据的列表,所以需要把列表里的每一条bytes的数据先解码为字符串,再用JSON模块解析为字典
- 第19行:将最终生成的包含字典的列表返回
获取聊天信息
修改完成代码以后重启网站,可以看到聊天室消息还是一片空白。现在,人工向Redis中添加几天数据:
01 127.0.0.1:6379> lpush chat_list '{"msg": "我是人工添加的消息", "nick": "青南", "post_time": "2018-07-22 16:15:00"}' 02 (integer) 1 03 127.0.0.1:6379> lpush chat_list '{"msg": "我是青南的助手", "nick": "青南的助手", "post_time": "2018-07-22 16:15:00"}' 04 (integer) 2 05 127.0.0.1:6379>
在redis-cli中手动添加聊天信息
添加好聊天信息以后,可以看到聊天室里已经出现了手动添加的内容。
手动添加的内容已经出现在聊天窗口
2. 实现发送新信息的功能
发送新信息的原理非常简单,把新信息字典转换为JSON格式并存入chat_list列表的右侧即可。发现信息对应的方法为push_chat_info(),完善它的代码:
01 def push_chat_info(self, chat_info): 02 """ 03 把聊天信息存入列表的右侧。 04 05 使用Redis的集合实现,对应的Key为self.chat_list中保存的字符串。 06 把chat_info字典先转化为JSON字符串,再存入Redis中列表中。 07 08 为了防止列表消息太长,因此需要使用ltrim命令删除多余的信息,只保留 09 列表最右侧的20条 10 :param chat_info: 字典,格式为{'msg': '信息', 'nick': '青南', 'post_time': '2018-07-22 10:00:12'} 11 :return: None 12 """ 13 self.client.rpush(self.chat_list, json.dumps(chat_info)) 14 self.client.ltrim(self.chat_list, -20, -1)
其中,主要说明如下:
- 第13行:先把聊天信息对应的字典转换为JSON字符串。然后添加的列表的右侧
- 第14行:chat_list列表只保留最右侧的20条,多余的信息全部删除
这里引入一个新的知识点,列表的ltrim命令。这个命令的作用是从列表里面删除保留一段数据并删除其他数据。
代码第14行调用ltrim命令,传入了三个参数。
第一个参数是列表的Key。
第二个参数是保留数据起始位置,第三个参数是保留数据截至位置。所以这一行代码的作用是除了右边数第20条数据(含)到右边第一条数据(含)外,删除列表中的其他数据。
本项目使用ltrim是为了节约服务器内存,加速读取列表的时间。这并不是必需的,如果服务器内存足够的话,或者信息不多的话,也可以不删除。
实现发布新信息的功能
修改完成代码以后网站,可以看到发帖功能已经正常。
发帖功能开发完成
到目前为止基本功能已经开发完成,但还有一个小问题需要解决。
用户无限制刷屏
如果不限制同一个用户发送同一条信息的频率,可能会出现一个用户短时间发送大量相同信息的刷屏的行为。
3. 设定刷屏检查字符串
本项目将会限制同一个昵称发送完全相同信息的频率。两次完全相同的信息的发送时间间隔为120秒。
Redis天然就具有实现这一功能的能力。在Redis中,Key可以设置过期时间。时间到了以后Redis会自动删除这个Key。
那么如果设置一个字符串,Key为:“昵称-发言内容”。
例如“青南-我在灌水”。然后把这个Key的过期时间设置为120秒。这个字符串的值无所谓,随便设置什么都可以。那么只要这个Key在Redis中,就说明这个用户昵称,在120秒内已经发送过这条信息了。只要这个Key不在Redis中,就说明这个用户从来没有发送过这条信息或者发送已经超过了两分钟,Key被Redis自动删除了。
而且Redis可以使用ttl命令查询一个Key的过期时间还剩多少秒。这样还可以实现提醒功能。
设置Key的过期时间,对应的方法为set_nick_msg_expire_time()修改代码:
01 def set_nick_msg_expire_time(self, nick, msg): 02 """ 03 你需要实现这个方法 04 05 设定Key的过期时间,这个功能的目的是限定同一个用户在2分钟内不能发送同样的内容。 06 为了防止信息太长,因此把信息编码为md5以后再与昵称拼接以缩短Key的长度。 07 使用Redis的字符串实现,字符串的Key为昵称+信息的md5编码,值为1.使用set命令的 08 ex参数设定Key的过期时间为120秒,时间到了以后Redis会自动删除这个Key 09 :param nick: 昵称 10 :param msg: 信息 11 :return: None 12 """ 13 msg_md5 = hashlib.md5(msg.encode()).hexdigest() 14 duplicate_msg_check_flag = nick + msg_md5 15 self.client.set(duplicate_msg_check_flag, 1, ex=120)
第13行:先把信息转换为MD5。这样做的好处是缩短信息的长度,避免太长到信息导出超出Redis Key的限制
第14行:把用户昵称与消息的MD5值拼成一个长字符串,作为Key
第15行:在Redis中设定一个字符串,Key为昵称与消息的MD5值,值为1,通过ex参数设定过期时间为120,过期时间一到Redis就会删除这个Key
设定刷屏检查字符串及其过期时间
4、读取刷屏限制剩余时间
当用户要发送新内容的时候,网站先检查Redis是否有“昵称+新信息MD5值”这个Key,如果有,说明用户在120秒类发送了相同的内容,此时返回解除刷屏限制的剩余时间。如果不存在这个Key,就返回None。对应的方法为get_nick_msg_expire_time()。修改现有代码:
01 def get_nick_msg_expire_time(self, nick, msg): 02 """ 03 获取某一个昵称发送某一条消息的过期时间。这个功能的作用是 04 为了防止同一个用户短时间发送大量相同信息刷屏。 05 06 为了防止信息太长,因此把信息编码为md5以后再与昵称拼接以缩短Key的长度。 07 使用Redis的ttl命令来实现,ttl命令如果返回None,说明不存在这个Key, 08 返回None。如果ttl返回-1,说明这个Key没有设定过期时间,这个Key可以一直存在 09 如果ttl返回一个大于0的正整数,说明在这个整数对应的秒过于以后,Redis会自动 10 删除这个Key 11 12 :param nick: 昵称 13 :param msg: 信息 14 :return: None 或者 数字 15 """ 16 msg_md5 = hashlib.md5(msg.encode()).hexdigest() 17 duplicate_msg_check_flag = nick + msg_md5 18 expire_time = self.client.ttl(duplicate_msg_check_flag) 19 return expire_time
其中,主要说明如下:
- 第16行:获得消息的MD5值
- 第17行:把昵称与消息的MD5值拼成一个Key
- 使用Redis的ttl命令检查Key的剩余时间。如果Key不存在,返回None,如果Key没有过期时间,返回-1,如果Key有过期时间,返回剩余时间(正整数)
读取刷屏限制过期时间
修改完成以后重启网站,尝试连续发送相同的信息,发现得到网站提示。
提示不能在两分钟内发送同样的内容
5、总结
本系列推送通过开发简易聊天室网站来巩固Redis的基础知识。同时也引入了列表裁剪,Key添加过期时间与检查Key剩余过期时间这三个知识点。读者在开发的过程中,可以经常使用redis-cli观察Redis中的Key的变化情况,以便于更好理解代码和命令的作用。