《Redis实战》一2.1 登录和cookie缓存

2.1 登录和cookie缓存

每当我们登录互联网服务(比如银行账户或者电子邮件)的时候,这些服务都会使用cookie来记录我们的身份。cookie由少量数据组成,网站会要求我们的浏览器存储这些数据,并且在每次服务发出请求时再将这些数据传回给服务。对于用来登录的cookie,有两种常见的方法可以将登录信息存储在cookie里面:一种是签名(signed)cookie,另一种是令牌(token)cookie。

签名cookie通常会存储用户名,可能还有用户ID、用户最后一次成功登录的时间,以及网站觉得有用的其他任何信息。除了用户的相关信息之外,签名cookie还包含一个签名,服务器可以使用这个签名来验证浏览器发送的信息是否未经改动(比如将cookie中的登录用户名改成另一个用户)。

令牌 cookie会在cookie里面存储一串随机字节作为令牌,服务器可以根据令牌在数据库中查找令牌的拥有者。随着时间的推移,旧令牌会被新令牌取代。表2-1展示了签名cookie和令牌cookie的优点与缺点。

因为Fake Web Retailer没有实现签名cookie的需求,所以我们选择了使用令牌cookie来引用关系数据库表中负责存储用户登录信息的条目(entry)。除了用户登录信息之外,Fake Web Retailer还可以将用户的访问时长和已浏览商品的数量等信息存储到数据库里面,这样便于将来通过分析这些信息来学习如何更好地向用户推销商品。

一般来说,用户在决定购买某个或某些商品之前,通常都会先浏览多个不同的商品,而记录用户浏览过的所有商品以及用户最后一次访问页面的时间等信息,通常会导致大量的数据库写入。从长远来看,用户的这些浏览数据的确非常有用,但问题在于,即使经过优化,大多数关系数据库在每台数据库服务器上面每秒也只能插入、更新或者删除200~2000 个数据库行。尽管批量插入、批量更新和批量删除等操作可以以更快的速度执行,但因为客户端每次浏览网页都只更新少数几个行,所以高速的批量插入在这里并不适用。

因为Fake Web Retailer目前一天的负载量相对比较大——平均情况下每秒大约1200次写入,高峰时期每秒接近6000次写入,所以它必须部署10台关系数据库服务器才能应对高峰时期的负载量。而我们要做的就是使用Redis重新实现登录cookie功能,取代目前由关系数据库实现的登录cookie功能。

首先,我们将使用一个散列来存储登录cookie令牌与已登录用户之间的映射。要检查一个用户是否已经登录,需要根据给定的令牌来查找与之对应的用户,并在用户已经登录的情况下,返回该用户的ID。代码清单2-1展示了检查登录cookie的方法。

代码清单2-1 check_token()函数

对令牌进行检查并不困难,因为大部分复杂的工作都是在更新令牌时完成的:用户每次浏览页面的时候,程序都会对用户存储在登录散列里面的信息进行更新,并将用户的令牌和当前时间戳添加到记录最近登录用户的有序集合里面;如果用户正在浏览的是一个商品页面,那么程序还会将这个商品添加到记录这个用户最近浏览过的商品的有序集合里面,并在被记录商品的数量超过25个时,对这个有序集合进行修剪。代码清单2-2展示了程序更新令牌的方法。

代码清单2-2 update_token()函数

通过update_token()函数,我们可以记录用户最后一次浏览商品的时间以及用户最近浏览了哪些商品。在一台最近几年生产的服务器上面,使用update_token()函数每秒至少可以记录20 000件商品,这比Fake Web Retailer高峰时期所需的6000次写入要高3倍有余。不仅如此,通过后面介绍的一些方法,我们还可以进一步优化update_token()函数的运行速度。但即使是现在这个版本的update_token()函数,比起原来的关系数据库,性能也已经提升了10~100倍。

因为存储会话数据所需的内存会随着时间的推移而不断增加,所以我们需要定期清理旧的会话数据。为了限制会话数据的数量,我们决定只保存最新的1000万个会话。①清理旧会话的程序由一个循环构成,这个循环每次执行的时候,都会检查存储最近登录令牌的有序集合的大小,如果有序集合的大小超过了限制,那么程序就会从有序集合里面移除最多100个最旧的令牌,并从记录用户登录信息的散列里面,移除被删除令牌对应的用户的信息,并对存储了这些用户最近浏览商品记录的有序集合进行清理。与此相反,如果令牌的数量未超过限制,那么程序会先休眠1秒,之后再重新进行检查。代码清单2-3展示了清理旧会话程序的具体代码。

代码清单2-3 clean_sessions()函数

让我们通过计算来了解一下,这段简单的代码为什么能够妥善地处理每天500万人次的访问:假设网站每天有500万用户访问,并且每天的用户都和之前的不一样,那么只需要两天,令牌的数量就会达到1000万个的上限,并将网站的内存空间消耗殆尽。因为一天有24×3600=86 400秒,而网站平均每秒产生5 000 000/86 400<58个新会话,如果清理函数和我们之前在代码里面定义的一样,以每秒一次的频率运行的话,那么它每秒需要清理将近60个令牌,才能防止令牌数量过多的问题发生。但是实际上,我们定义的令牌清理函数在通过网络来运行时,每秒能够清理10 000多个令牌,在本地运行时,每秒能够清理60 000多个令牌,这比所需的清理速度快了150~1000倍,所以因为旧令牌过多而导致网站空间耗尽的问题不会出现。

在哪里执行清理函数? 本书会包含一些类似代码清单2-3的清理函数,它们可能会像代码清单2-3那样,以守护进程的方式来运行,也可能会作为定期作业(cron job)每隔一段时间运行一次,甚至在每次执行某个操作时运行一次(例如,6.3节就在一个获取锁操作里面包含了一个清理操作)。一般来说,本书中包含while not QUIT:代码的函数都应该作为守护进程来执行,不过如果有需要的话,也可以把它们改成周期性地运行。

Python传递和接收可变数量参数的语法 代码清单2-3用到了3次类似conn.delete (*vtokens)这样的语法。简单来说,这种语法可以直接将一连串的多个参数传入函数里面,而不必先对这些参数进行解包(unpack)。要了解关于这一语法的更多信息,请通过以下短链接访问《Python语言教程》的相关章节:http://mng.bz/8I7W

Redis 的过期数据处理 随着对Redis的了解逐渐加深,读者应该会慢慢发现本书展示的一些解决方案有时候并不是问题的唯一解决办法。比如对于这个登录cookie例子来说,我们可以直接将登录用户和令牌的信息存储到字符串键值对里面,然后使用Redis的EXPIRE命令,为这个字符串和记录用户商品浏览记录的有序集合设置过期时间,让Redis在一段时间之后自动删除它们,这样就不需要再使用有序集合来记录最近出现的令牌了。但是这样一来,我们就没有办法将会话的数量限制在1000万之内了,并且在将来有需要的时候,我们也没办法在会话过期之后对被废弃的购物车进行分析了。

熟悉多线程编程或者并发编程的读者可能会发现代码清单2-3展示的清理函数实际上包含一个竞争条件(race condition):如果清理函数正在删除某个用户的信息,而这个用户又在同一时间访问网站的话,那么竞争条件就会导致用户的信息被错误地删除。目前来看,这个竞争条件除了会使得用户需要重新登录一次之外,并不会对程序记录的数据产生明显的影响,所以我们暂时先搁置这个问题,之后的第3章和第4章会说明怎样防止类似的竞争条件发生,并进一步加快清理函数的执行速度。

通过使用Redis来记录用户信息,我们成功地将每天要对数据库执行的行写入操作减少了数百万次。虽然这非常的了不起,但这只是我们使用Redis构建Web应用程序的第一步,接下来的一节将向读者们展示如何使用Redis来处理另一种类型的cookie。

时间: 2024-09-19 23:58:21

《Redis实战》一2.1 登录和cookie缓存的相关文章

《Redis实战》一导读

前 言 Redis实战Chris Testa是我在圣莫尼卡Google分部工作时认识的一个朋友,我从2010年3月开始和他一起在加利福尼亚州贝弗利山的一间小创业公司工作,Chris是公司的领头和主管,而我则受聘于他成为了公司研究部门的架构师. 在对某个不相关的问题进行了一个下午的讨论之后,Chris向我推荐了Redis,他认为我这个理论计算机科学专业毕业的人应该会对这个数据库感兴趣.在使用Redis并按照自己的想法对Redis打补丁几个星期之后,我开始参与邮件列表里面的讨论,并向其他人提供建议或

《Redis实战》一第2章 使用Redis构建Web应用

第2章 使用Redis构建Web应用 Redis实战本章主要内容 登录cookie 购物车cookie 缓存生成的网页 缓存数据库行 分析网页访问记录 前面的第1章对Redis的特性和功能做了简单的介绍,本章将紧接上一章的步伐,通过几个示例,对一些典型的Web应用进行介绍.尽管本章展示的问题比起实际情况要简单得多,但这里给出的网络应用实际上只需要进行少量修改就可以直接应用到真实的程序里面.本章的主要任务是作为一个实用指南,告诉你可以使用Redis来做些什么事情,而之后的第3章将对Redis命令进

《Redis实战》一2.2 使用Redis实现购物车

2.2 使用Redis实现购物车 网景(Netscape)公司在20世纪90年代中期最先在网络中使用了cookie,这些cookie最终变成了我们在上一节讨论的登录会话cookie.cookie最初的意图在于为网络零售商(web retailer)提供一种购物车,让用户可以收集他们想要购买的商品.在cookie之前,有过几种不同的购物车解决方案,但这些方案全都不太好用. 使用cookie实现购物车--也就是将整个购物车都存储到cookie里面的做法非常常见,这种做法的一大优点是无须对数据库进行写

《Redis实战》一2.6 小结

2.6 小结 本章介绍了几种用于降低Fake Web Retailer的数据库负载和Web服务器负载的方法,这些例子里面介绍的都是真实的Web应用程序当今正在使用的思路和方法. 本章希望向读者传达这样一个概念:在为应用程序创建新构件时,不要害怕回过头去重构已有的构件,因为就像本章展示的购物车cookie的例子和基于登录会话cookie实现网页分析的例子一样,已有的构件有时候需要进行一些细微的修改才能真正满足你的需求.本书之后的章节也会继续引入新的主题,并且偶尔会回过头去审视之前介绍过的主题,对它

《Redis实战》一第1章 初识Redis

第1章 初识Redis Redis实战 本章主要内容 Redis与其他软件的相同之处和不同之处 Redis的用法 使用Python示例代码与Redis进行简单的互动 使用Redis解决实际问题 Redis是一个远程内存数据库,它不仅性能强劲,而且还具有复制特性以及为解决问题而生的独一无二的数据模型.Redis提供了5种不同类型的数据结构,各式各样的问题都可以很自然地映射到这些数据结构上:Redis的数据结构致力于帮助用户解决问题,而不会像其他数据库那样,要求用户扭曲问题来适应数据库.除此之外,通

php用户登录之cookie信息安全分析_php技巧

本文实例讲述了php用户登录之cookie信息安全.分享给大家供大家参考,具体如下: 大家都知道用户登陆后,用户信息一般会选择保存在cookie里面,因为cookie是保存客户端,并且cookie可以在客户端用浏览器自由更改,这样将会造成用户cookie存在伪造的危险,从而可能使伪造cookie者登录任意用户的账户. 下面就说说平常一些防止用户登录cookie信息安全的方法: 一.cookie信息加密法 cookie信息加密法即用一种加密方法,加密用户信息,然后在存入cookie,这样伪造者即使

java 模拟登录 cookie-java模拟登录记住cookie还是无法访问须登录才能访问的页面

问题描述 java模拟登录记住cookie还是无法访问须登录才能访问的页面 用HttpURlConnection实现模拟登录,获取的cookie为 ASP.NET_SessionId=2gajq4a3qlb5ewhb4n5ot04u; path=/; HttpOnly 使用该cookie为什么还是不能访问需要登录才能访问的页面

《Redis实战》一2.4 数据行缓存

2.4 数据行缓存 到目前为止,我们已经将原本由关系数据库和网页浏览器实现的登录和访客会话转移到了Redis上面实现:将原本由关系数据库实现的购物车也放到了Redis上面实现:还将所有页面缓存到了Redis里面.这一系列工作提升了网站的性能,降低了关系数据库的负载并减少了网站成本. Fake Web Retailer的商品页面通常只会从数据库里面载入一两行数据,包括已登录用户的用户信息(这些信息可以通过AJAX动态地载入,所以不会对页面缓存造成影响)和商品本身的信息.即使是那些无法被整个缓存起来

《Redis实战》一第一部分 入门

第一部分 入门 Redis实战本书最开始的两章将对Redis进行介绍,并展示Redis的一些基本用法.读完这两章之后,读者应该能够用Redis对自己的项目进行一些简单的优化.