session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(map)来保存信息。
当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否包含了一个session标识-称为session id,如果已经包含一个session id则说明以前已经为此客户创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个,这种情况可能出现在服务端已经删除了该用户对应的session对象,但用户人为地在请求的URL后面附加上一个JSESSION的参数)。如果客户请求不包含session id,则为此客户创建一个session并且同时生成一个与此session相关联的session id,这个session id将在本次响应中返回给客户端保存。
目前Go标准包没有为session提供任何支持,我们将会自己动手来实现go版本的session管理和创建。
session的创建过程
先想一下,首先我们要自己定义一个结构来存储用户信息,且要有一个对应的id来标识用户对应的这个结构。
这个结构可以先定义为
type SessionStore struct {sid string //session id唯一标示 timeAccessed time.Time //最后访问的时间value map[interface{}]interface{} //session里面存储的值}
既然已经定义了结构体,肯定要有对应的方法或接口来供外部使用,无非就是设置值,读取值,获取值,以及获取id。
type Session interface { Set(key, value interface{}) error Get(key interface{}) interface{} Delete(key interface{}) error SessionID() string}
session部分大致定义好了;服务器要存储许多用户的session,可以直接再内存中存储,但这种情况下,系统一旦掉电,所有的会话数据就会丢失。如果是电子商务类网站,这将造成严重的后果。所以为了解决这类问题,你可以将会话数据写到文件里或存储在数据库中,当然这样会增加I/O开销,但是它可以实现某种程度的session持久化,也更有利于session的共享。这里我们使用内存存储的形式。
服务端如何在内存中存储这些session,可以采用链表的形式(采用go包 "container/list",文章末介绍)。
type MProvider struct { lock sync.Mutex //用来锁 sessions map[string]*list.Element //用来存储在内存 list *list.List //用来做gc,即管理这些session,如检查这些session最后一次访问时间到现在是否 //超过我们会话所设置的时间}
内存存储只是一种存储形式,每种存储方式都有它们自己的实现(MProvider是内存存储),因此我们要提供一种统一的接口供外部使用。接口功能可以抽象为创建session,返回id对应的session,销毁id对应的session,以及删除过期的session。
type Provider interface { SessionInit(sid string) (Session, error) SessionRead(sid string) (Session, error) SessionDestroy(sid string) error SessionGC(maxLifeTime int64)}
通过Provider接口提供的方法可以操作Session,在COOKIE那一章提到生产的session通过使用COOKIE机制来返回对应的id。回忆一下COOKIE的结构体,COOKIE.Value就是id,还需要Name。之前使用的COOKIE是当时间到时浏览器自动清除,在这里我们不通过COOKIE.Expires来设置过期时间,我们自己设置一个时间长度在服务端来检查并清除过期的session。
①