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

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:

本节书摘来异步社区《Redis实战》一书中的第2章,第2.1节,作者: 【美】Josiah L. Carlson(约西亚 L.卡尔森)译者: 黄健宏 责编: 杨海玲,更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.1 登录和cookie缓存

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

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

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


b1

因为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()函数


q3


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

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


q2

通过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()函数


q1

让我们通过计算来了解一下,这段简单的代码为什么能够妥善地处理每天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。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
NoSQL 关系型数据库 Redis
DMS产品常见问题之dms登录redis实例时报错如何解决
DMS(数据管理服务,Data Management Service)是阿里云提供的一种数据库管理和维护工具,它支持数据的查询、编辑、分析及安全管控;本汇总集中了DMS产品在实际使用中用户常遇到的问题及其相应的解答,目的是为使用者提供快速参考,帮助他们有效地解决在数据管理过程中所面临的挑战。
|
26天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
60 0
|
1天前
|
存储 缓存 运维
软件体系结构 - 缓存技术(5)Redis Cluster
【4月更文挑战第20天】软件体系结构 - 缓存技术(5)Redis Cluster
108 10
|
9天前
|
缓存 NoSQL Java
使用Redis进行Java缓存策略设计
【4月更文挑战第16天】在高并发Java应用中,Redis作为缓存中间件提升性能。本文探讨如何使用Redis设计缓存策略。Redis是开源内存数据结构存储系统,支持多种数据结构。Java中常用Redis客户端有Jedis和Lettuce。缓存设计遵循一致性、失效、雪崩、穿透和预热原则。常见缓存模式包括Cache-Aside、Read-Through、Write-Through和Write-Behind。示例展示了使用Jedis实现Cache-Aside模式。优化策略包括分布式锁、缓存预热、随机过期时间、限流和降级,以应对缓存挑战。
|
16天前
|
存储 缓存 NoSQL
使用redis进行缓存加速
使用redis进行缓存加速
26 0
|
16天前
|
存储 监控 NoSQL
使用Redis的Bitmap统计一周连续登录的用户
使用Redis的Bitmap统计一周连续登录的用户
9 1
|
17天前
|
存储 缓存 NoSQL
Java手撸一个缓存类似Redis
`LocalExpiringCache`是Java实现的一个本地缓存类,使用ConcurrentHashMap存储键值对,并通过ScheduledExecutorService定时清理过期的缓存项。类中包含`put`、`get`、`remove`等方法操作缓存,并有`clearCache`方法来清除过期的缓存条目。初始化时,会注册一个定时任务,每500毫秒检查并清理一次过期缓存。单例模式确保了类的唯一实例。
13 0
|
26天前
|
缓存 应用服务中间件 数据库
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(多级缓存设计分析)
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(多级缓存设计分析)
30 1
|
28天前
|
NoSQL Redis
Netty实战:模拟Redis的客户端
Netty实战:模拟Redis的客户端
13 0
|
1月前
|
存储 NoSQL Redis
KubeSphere 核心实战之二【在kubesphere平台上部署redis】(实操篇 2/4)
KubeSphere 核心实战之二【在kubesphere平台上部署redis】(实操篇 2/4)
22 0