背景:最近看了labuladong大佬的一篇关于session实现的文章,类似的文章比较少,分享给大家
session的组成
session一般由三个组件配合完成,分别是Manager Provider Session三个类(接口)
-
浏览器通过
HTTP协议向服务器请求路径/content的网页资源,对应路径上Handler函数接受请求,解析HTTP header中的cookie,得到其中存储的sessionID,然后把这个ID发给Manager -
Manager充当一个session管理器的角色,主要存储一些配置信息,比如session的存活时间,cookie的名字等等。而所有的session存在Manager内部的一个Provider中。所以Manager会把sid传递给Provider,让它去找这个ID对应的具体是哪个session。 -
Provider就是一个容器,最常见的应该就是一个散列表(hash表),将每个sid和对应的session一一映射起来。收到Manager传递的sid之后,它就找到sid对应的session结构,也就是Session结构,然后返回它 -
Session中存储着用户的具体信息,由Handler函数中的逻辑拿出这些信息,生成该用户的HTML网页,返回给客户端。
为什么不直接在Handler函数中搞一个哈希表,然后存储
sid和Session结构的映射呢?
这就是设计层面的技巧了,下面就来说说,为什么分成Manager、Provider、Session。
先从最底层的Session说,既然session就是键值对,为什么不直接用哈希表,而是要抽象出这么一个数据结构呢?
- 因为
Session结构可能不止存储了一个哈希表(其实这儿我有个疑问是这个session结构到底是什么样的呢?用键值对来描述有点太浅薄了,如果有大佬明白欢迎在留言指出),还可以存储一些辅助数据,比如sid,访问次数,过期时间或者最后一次的访问时间,这样便于实现LRU、LFU这样的算法。
LRU,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
LFU,即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
- 因为session可以有
不同的存储方式。如果用编程语言内置的哈希表,那么session数据就是存储在内存中,如果数据量大,很容易造成程序包崩溃,而且一旦程序结束,所有session数据都会丢失。所以可以有很多种session的存储方式,比如存入缓存数据库Redis、Mysql等等
因此,Session结构提供一层抽象,屏蔽不同存储方式的差异,只提供一组通用接口操作键值对:
type Session interface {
// 设置键值对
Set(key, val interface{})
// 获取 key 对应的值
Get(key interface{}) interface{}
// 删除键 key
Delete(key interface{})
}
再说Provider为啥要抽象出来。上面那个图的Provider就是一个散列表,保存sid到Session的映射,但是实际中肯定会更加复杂。我们需要时不时删除一些session,除了设置存活时间之外,还可以采用一些其他策略,比如LRU缓存淘汰算法,这样就需要Provider内部使用哈希链表这种数据结构来存储session。
因此,Provider作为一个容器,就是要屏蔽算法细节,以合理的数据结构和算法组织sid和Session的映射关系,只需要实现下面这几个方法实现对session的增删改查:
type Provider interface {
// 新增并返回一个 session
SessionCreate(sid string) (Session, error)
// 删除一个 session
SessionDestroy(sid string)
// 查找一个 session
SessionRead(sid string) (Session, error)
// 修改一个session
SessionUpdate(sid string)
// 通过类似 LRU 的算法回收过期的 session
SessionGC(maxLifeTime int64)
}
最后说Manager,大部分具体工作都委托给Session和Provider承担了,Manager主要就是一个参数集合,比如session存活时间,清理过期session的策略,以及session的可用存储方式。Manager屏蔽了操作的具体细节,我们可以通过Manager灵活地配置session机制。
综上,session 机制分成几部分的最主要原因就是 解耦,实现定制化