1 检查会话

当我们登录互联网服务(比如银行账户或者电子邮件)的时候.这些服务都会使用cookie来记录我们的身份。cookie又少量数据组成,网站会要求我们的浏览器存储这些数据, 并在每次服务发送请求时将这些数据传回给服务。对于用来登录的cookie.有两种常见的方 法可以将登录信息存储在cookie里面:一种是签名(signed ) cookie,另一种是令牌(token) cookie。
签名cookie通常会存储用户名,可能还有用户ID、用户最后一次成功登录的时间.以及网 站觉得有用的其他任何信息除了用户的相关信息之外.签名。cookie还包含一个签名,服务器 可以使用这个签名来验证浏览器发送的信息是否未经改动(比如将cookie中的登录用户名改成 另一个用户)。
令牌cookie会在cookie里面存储一串随机字节作为令牌,服务器可以根据令牌在数据库中 查找令牌的拥有者。随着时间的推移.旧令牌会被新令牌取代。表2-1展示了签名cookie和令牌 cookie的优点与缺点。

redis保存session,spring redis保存用户登录状态_有序集合


用关系数据库表中负责存储用户登录信息的条目(entry)。除了用户登录信息之外,Fake Web Retaile:还可以将用户的访问时长和已浏览商品的数l迁等信息存储到数据库里面,这样便于将来 通过分析这些信息来学习如何更好地向用户推梢商品。

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

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

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

def check_token(conn,token){
	return conn.hget("login",token)
}

2 更新会话

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

def update_token(conn,token,user,item=None){
	timestamp = time.time()
	conn.hset("login:",token,user)
	conn.zadd("recent:",token,timestamp )
	if item
		conn.zadd("viewed:"+token,item,timestamp)
		conn.zremrangebyrank("viewed:"+token,0,-26)
}

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


3 清理会话

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

redis保存session,spring redis保存用户登录状态_数据_02


让我们计算下,该代码为什么可以处理每天500w人次的访问:假设每天有500W用户访问,并且每个用户和之前的都不一样,那么只需2天,令牌数量就会达到1kw的上线,网站内存空间耗尽,因为如果一天由 86400s,网站每秒产生 500w/ 86400 < 58个新会话,如果清理函数每秒执行一次的话,那么他每秒需要清理将近60个令牌,才能防止内存消耗完。但实际上,我们定义的令牌清理函数在通过网络运行,每秒可以处理1w个令牌,在本地运行时候,每秒可以清理6w个令牌,比所欲的清理速度快了 150~1k倍。