session模块在go语言中的实现

正在为Words Review 实现云端同步的功能,需要在服务器端维护在线用户的信息,这就需要session的管理。 Web.go 没有实现session模块, go dashboard 上有一个session的实现,但build状态是不通过,而且看源码实现的太复杂。

我没有去仔细翻看session的相关标准,只是基于我之前对web应用中cookie session的理解, 一个基本的session模块只需要能够针对每个session identifier正确的存取数据,管理生命周期就可以了。

下面是在golang中的简单实现, 你可以在github上找到源代码。

首先定义session实例的数据结构。 一个session对象就是一个字符串,存储这个session的id, 由于golang不允许对系统内置数据类型添加方法,我们需要一个自定义类型:

type Session string

出于基本的安全考虑,session id需要唯一, 不可预测。 GUID是个不错的选择, 下面这个函数用来产生session id。

golang还没有官方的windows支持,目前的go语言版本调用windows api好像还有问题, 而且我的site也不打算跑在windows下面。 所以windows下guid暂时先用时间戳加随机数的方式, linux下直接读取/dev/urandom

func newGuid() (string, os.Error) {
    if syscall.OS == "windows" {
        //no /dev/urandom on windows
        //TODO: use some windows api instead ?
        guid := getMd5Hex(time.UTC().Format(time.ANSIC) + strconv.Itoa64(rand.Int63()))
        return guid, nil
    }

    f, err := os.Open("/dev/urandom")
    if err != nil {
        return "", err
    }
    defer f.Close()

    b := make([]byte, 16)
    _, err = f.Read(b)
    if err != nil {
        return "", err
    }
    uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
    return uuid, nil
}

生命周期的管理使用 goroutines 和 channels, 在指定时间后删除session:

func New() Session {
    return NewTimeout(sessionTimeout)
}

func NewTimeout(timeout int64) Session {
    sid, err := newGuid()
    if err != nil {
        panic(err)
    }
    sessions[sid] = make(map[string]interface{})
    t := time.NewTimer(timeout * 1e9) //timer接受的是nanoseconds
    go startSession(t.C, sid) 
    return Session(sid)
}

func startSession(c <-chan int64, sid string) {
    <-c
    sessions[sid] = nil, false
}

数据的存取就比较简单了,使用一个package level可见的map变量即可简单实现。

用法示例:

创建新的session:

s := session.New() //默认过期时间1小时

s := session.NewTimeout(20 * 60)  // 20 分钟后过期

存取session数据:

err := s.Put("Username", "Bob")
err  = s.Put("Logintime", time.UTC())

v, err :=  s.Get("Logintime") //return interface{}, need type assertion 
loginTime := v.(*time.Time)

结合web.go:

s := session.New()
ctx.SetCookie("u", string(s), 3600)  //set session-cookie
err := s.Put("user", u)

关于session肯定还有很多细节没有处理,但满足我现在的需求了。

Comments

  1. Patxi

    That hits the target dead cneter! Great answer!

Leave a comment

*
* (Won't be published)