首页

6.3项目开发过程

关灯 护眼    字体:

上一章 目录 下一章




6.3.1  实现登录功能1:创建Redis的连接实例


1.登录过程的逻辑原理

(1)用户输入昵称并单击“登录”按钮。

(2)网站在Redis集合中检查昵称是否存在:

●  如果昵称存在,则提示用户昵称已称存在,不能登录。

●  如果昵称不存在,则把昵称添加到集合中,防止其他人再使用这个昵称。

(3)基于昵称与当前时间戳生成Token。

(4)把昵称与Token保存到Redis中,以便再次查询。

(5)把昵称与Token设置到浏览器Cookies中,以便今后进入聊天界面时免去登录过程。

2.具体实现过程

要使用Redis,首先需要创建Redis的连接实例。把创建连接实例的代码写在__init__()方法中,以便在整个RedisUtil类中进行调用。

修改RedisUtil类的__init__()方法,连接本地到Redis,见代码6-1。

代码6-1  初始化Redis连接实例

其中,主要代码说明如下。

●  第2~4行代码:初始化一些固定的字符串,这些字符串将要作为Redis的Key使用。

●  第7行代码:连接本地Redis。

修改后的代码如图6-10所示。

图6-10  连接本地Redis



6.3.2  实现登录功能2:实现“检查昵称是否重复”功能


要检查昵称是否重复,则用到的是Redis的“集合”这个数据结构。

向集合中添加一条内容:

●  如果返回1,则表示这条内容原来不在集合中。

●  如果返回0,则表示集合里面已经有这条内容了。

1.实现“判断昵称是否重复”的方法

根据is_nick_already_exists()方法注释的提示,完善这个方法。见代码6-2。

代码6-2  使用Redis集合判断昵称是否重复

其中,主要代码说明如下:

●  第12行,调用集合的“sad”命令,把昵称添加到Key为self.chat_room_nick_set这个属性值的集合中。

●  第13~15行,判断Redis返回的数字。如果返回数字为1,则表示原来集合没有这个昵称,此时这个方法需要返回  False;如果返回的数字不是1,则说明原来已经有这个昵称了,这个方法就会返回True。

修改完成的代码如图6-11所示。

图6-11  修改is_nick_already_exists方法

2.测试“昵称判重”功能

修改完成后,重启网站,再次尝试登录。

可以发现,验证昵称是否重复的功能已经正常。不会再出现输入任何昵称都提示昵称已经存在的问题了。但问题是,即使输入一个全新的昵称,网站也不会进入聊天室页面,而是闪一下后继续留在登录页面。



6.3.3  实现登录功能3:实现“设置和获取Token”功能


所谓Token,本质上就是一段用来验证身份的字符串。

在本项目中,Token是昵称加上当前时间戳并转换为MD5后的值。

设置与获取  Token  对应的是  set_token()方法和  get_token()方法。这两个方法本质上就是在Redis添加字符串和读取字符串。

保存Token的字符串,Key为“cookie-昵称”(例如“cookie-王小一”),字符串的值为Token,每一个昵称对应一个字符串。

1.实现设置Token的方法

修改set_token()方法后的代码如下:

代码6-3  使用Redis字符串记录Token信息



其中,主要代码说明如下。

●  第11行代码:拼接出完整的字符串Key。

●  第12行代码:在Redis中设置Key和对应的Token。

2.实现获取Token的方法

修改get_token方法后的代码如下:

代码6-4  从Redis中读取Token信息

其中,主要代码说明如下。

●  第12行代码:拼出这个昵称对应的字符串Key。

●  第13行代码:从Redis中读取这个Key的值。

●  第14行代码:如果这个Key不存在,则返回None;如果Key存在,则把Key对应的bytes型的数据解码为字符串后返回。

修改后的代码如图6-12所示。

图6-12  修改set_token()方法和get_token()方法

3.测试登录功能

修改完成后,重启网站,再次尝试使用新的名字登录,发现已经可以正常进入聊天室页面了,如图6-13所示。

图6-13  成功登录聊天室页面

但是聊天窗口始终没有任何信息显示,发送信息也没有效果。

提示:

为什么需要Token?

网页基于HTTP协议,而HTTP协议是没有状态的。什么叫作没有状态?就是某个用户访问了两个页面,但是网站并不知道这两次访问来自同一个人。该用户访问登录页面是一次请求,访问聊天室页面是另一个请求,网站怎么知道访问聊天室的这个人就是刚刚登录的那个人呢?

为了让这种“没有状态”变得“有状态”,就引入了一个叫作Cookies的东西。Cookies本质上是一小段文本信息,浏览器发送每一个请求都带上这段文本信息,于是网站看到两次请求都有相同的Cookies,就知道这两次请求来自同一个人。

例如,一个用户用昵称“青南”进行登录,网站收到这个登录请求后,就给这个浏览器返回一段Cookies:“这个人是青南”。然后,浏览器每次访问这个网站的其他页面都会带上这一段话。当这个用户访问聊天室页面时,网站先检查Cookies,发现Cookies中有“这个人是青南”,所以网站就知道这个用户之前是登录过的,直接让他浏览聊天室页面。

如果现在来了一个坏人,他先用“坏人”这个昵称登录网站,网站本来返回给他的Cookies是:“这个人是坏人”。但是这个坏人强行把浏览器的  Cookies  修改了,改成了“这个人是青南”。于是他就可以用青南的身份招摇撞骗。这叫作“Cookies欺骗”。

为了防止  Cookies  欺骗,网站在用户第一次请求时,会根据用户昵称和当前时间戳生成一个密码,网站先把这个密码保存到自己身边,然后再设置到这个用户的浏览器Cookies中。这样一来,昵称和密码必需一一对应才能正常访问网站。当用户再次访问聊天室页面时,网站会从  Cookies  中读出昵称和这个密码,然后与自己保存的密码进行对比,发现匹配才让这个用户正常访问聊天室页面。这就是防止Cookies欺骗最简单的办法。因为修改昵称很容易,但是知道这个密码就很难。

这个密码就是Token。

Token与我们平时的银行卡密码,QQ密码不同的地方在于:

●  Token是网站生成并返回给我们的,我们只需要记住就可以了。

●  银行卡密码和QQ密码是我们自己生成的。



6.3.4  实现聊天室页面1:实现“获取聊天消息”功能


从本小节开始实现聊天室页面。

1.开发Redis列表中获取聊天消息的方法

聊天消息保存在Redis中名为“chat_list”的列表中,新的消息在列表右侧,旧的消息在列表左侧。每次返回最右侧的20条信息。

获取聊天消息对应的方法为get_chat_list()。修改这个方法可以获取消息列表,见代码6-5。

代码6-5  获取聊天记录

其中,主要代码说明如下。

●  第14行代码:使用列表的“lrange”命令获取但不删除列表中的信息。-20表示从右往左数第20条信息,-1表示最右边的信息。

●  第16~18行代码:由于lrange返回的数据是包含bytes型数据的列表,所以需要把列表里的每一条bytes的数据先解码为字符串,再用JSON模块解析为字典。

●  第19行代码:将最终生成的包含字典的列表返回。

修改后的代码如图6-14所示。

图6-14  获取聊天信息

2.手动添加测试数据

修改完成代码后重启网站,可以看到聊天室消息还是一片空白。现在,人工向  Redis  中添加几行数据:

代码6-6  手动向Redis中添加聊天信息

在redis-cli手动添加聊天信息,如图6-15所示。

图6-15  在redis-cli中手动添加聊天信息

添加好聊天信息后,可以看到聊天室里已经出现了手动添加的内容,如图6-16所示。

图6-16  手动添加的内容已经出现在聊天窗口



6.3.5  实现聊天室页面2:实现“发送新信息”功能


发送新信息的原理非常简单,把新信息字典转换为JSON格式并存入chat_list列表的右侧即可。

1.实现发送消息的方法

发送消息对应的方法为push_chat_info(),下面完善它的代码:

代码6-7  实现发送新信息的功能



其中,主要代码说明如下。

●  第13行代码:先把聊天信息对应的字典转换为JSON字符串,然后添加到列表的右侧。

●  第14行代码:chat_list列表只保留最右侧的20条,将多余的信息全部删除。

提示:

这里引入一个新的知识点——列表的“ltrim”命令。

这个命令的作用是从列表里面删除保留一段数据并删除其他数据。

代码第14行调用“ltrim”命令,传入了3个参数:

●  第1个参数是列表的Key。

●  第2个参数是保留数据起始位置。

●  第3个参数是保留数据截至位置。

所以,这一行代码的作用是:除了从右边开始数第20条数据(含)到右边第1条数据(含)外,删除列表中的其他数据。

在本项目中,使用“ltrim”是为了节约服务器内存,加速读取列表的时间。这并不是必需的。如果服务器内存足够或者信息不多,也可以不删除。

修改后的代码如图6-17所示。

图6-17实现发布新信息的功能

2.测试发消息功能

修改完成代码后网站,可以看到发帖功能已经正常,如图6-18所示。

图6-18  发帖功能开发完成

到目前为止,基本功能都已经开发完成。但还有一个小问题需要解决:如果不限制同一个用户发送同一条信息的频率,可能会出现一个用户短时间发送大量相同信息的刷屏的行为(如图6-19所示)。

图6-19  用户无限制刷屏



6.3.6  实现聊天室页面3:设定“刷屏检查字符串”


本项目将会限制同一个昵称发送完全相同信息的频率。两次完全相同的信息的发送时间间隔不少于120秒。

1.理解限制刷屏的原理

Redis天然就具有实现这一功能的能力。在Redis中,Key可以设置过期时间。时间到了后Redis会自动删除这个Key。

那么如果设置一个字符串呢?Key为:“昵称-发言内容”,例如“青南-我在灌水”。然后把这个Key的过期时间设置为120秒。这个字符串的值无所谓,随便设置为什么都可以。那么:如果要这个Key在Redis中,则说明这个用户昵称在120秒内已经发送过这条信息了;如果这个Key不在Redis中,则说明这个用户从来没有发送过这条信息,或者发送已经超过了两分钟,  Key被Redis自动删除了。

而且,Redis可以使用“ttl”命令查询一个Key的过期时间还剩多少秒,这样还可以实现提醒功能。

2.实现限制刷屏的方法

设置Key的过期时间的方法为set_nick_msg_expire_time(),修改代码如下:

代码6-8  实现防止刷屏的功能



其中,主要代码说明如下。

●  第13行代码:先把信息转换为MD5。这样做的好处是:缩短信息的长度,避免太长以致于导出超出Redis  Key的限制。

●  第14行代码:把用户昵称与消息的MD5值拼成一个长字符串,作为Key。

●  第15行代码:在Redis中设定一个字符串,Key为“昵称+消息的MD5值”,值为1;通过ex参数设定过期时间为120,过期时间一到Redis就会删除这个Key。

修改后的代码如图6-20所示。

图6-20  设定刷屏检查字符串及其过期时间



6.3.7  实现聊天室页面4:读取刷屏限制的剩余时间


当用户要发送新内容时,网站先检查Redis是否有“昵称+新信息MD5值”这个Key。

●  如果有,则说明用户在120秒内发送了相同的内容。此时返回解除刷屏限制的剩余时间。

●  如果没有,则返回None。

1.设置查询限制刷屏时间的方法

对应的方法为get_nick_msg_expire_time()。完善以后的代码如下:

代码6-9  读取防止刷屏的剩余限制时间

其中,主要代码说明如下。

●  第16行代码:获得消息的MD5值。

●  第17行代码:把昵称与消息的MD5值拼成一个Key。

●  第18行代码:使用Redis的“ttl”命令检查Key的剩余时间。如果Key不存在,则返回None;如果Key没有过期时间,返回-1;如果Key有过期时间,返回剩余时间(正整数)。

修改后的代码如图6-21所示。

图6-21  读取刷屏限制过期时间

2.测试限制刷屏功能

修改完成后重启网站,尝试连续发送相同的信息,会得到网站提示,如图6-22所示。

图6-22  提示不能在两分钟内发送同样的内容



本章小结


本章通过开发简易聊天室网站来巩固Redis的基础知识。同时也引入了“列表裁剪”,“利用Key添加过期时间”与“检查Key剩余过期时间”这三个知识点。

读者在开发的过程中,可以经常使用redis-cli观察Redis中Key的变化情况,以便更好地理解代码和命令的作用。



第3篇  高级应用


在实际使用中,仅仅只有增、删、改、查功能是远远不能满足开发需求的。这就要求在学习Redis与MongoDB时,必需了解并掌握一些高级操作,以应对复杂的查询逻辑。

●  第7章会介绍MongoDB多个查询语句的逻辑组合方式,也会介绍如何查询特殊字段的内容。最后,会介绍MongoDB的精华内容之一——聚合查询。

●  第8章会介绍MongoDB的优化和安全建议,从而提高MongoDB的读写效率,并降低安全风险。

●  第9章会介绍Redis的另外几种数据结构和“发布/订阅”功能,这些更加高效的数据结构也正是Redis之所以高效的原因。


上一章 目录 下一章