双向链表 应用

2019-04-10  本文已影响0人  小小爱笑

前言

通过双向链表实现session的过期扫描。

双向链表

go 中实现为 list.List

type List struct {
    root Element // sentinel list element, only &root, root.prev, and root.next are used
    len  int     // current list length excluding (this) sentinel element
}

// Element is an element of a linked list.
type Element struct {
    // Next and previous pointers in the doubly-linked list of elements.
    // To simplify the implementation, internally a list l is implemented
    // as a ring, such that &l.root is both the next element of the last
    // list element (l.Back()) and the previous element of the first list
    // element (l.Front()).
    next, prev *Element

    // The list to which this element belongs.
    list *List

    // The value stored with this element.
    Value interface{}
}

实例

web开发中服务端需要维护session, session的实现可以有多种方式。例如内存,文件,数据库,redis 等。所以,对于不同的实现,抽象出Provider接口,实现有MemProvider FileProvider RedisProvider ...
另外,session通常有过期时间,所以需要服务端定期回收过期session。

provider接口定义如下:

type Provider interface {
    SessionInit(gclifetime int64, config string) error
    SessionRead(sid string) (Store, error)
    SessionExist(sid string) bool
    SessionDestroy(sid string) error
    SessionAll() int //get all active session
    SessionGC()
}

以内存session provider为例,通常通过map保存session,通过map而不是list,因为map保证了查找时间是o(1), 即SessionRead 通过session id 获取session。

type MemProvider struct {
    lock        sync.RWMutex             // locker
    sessions    map[string]*list.Element // map in memory
    list        *list.List               // for gc
    maxlifetime int64
}

如果只有map保存数据,当每次定期清理过期session时,需要遍历 时间复杂度为o(n)。所以为了提高清理效率,使用list维护记录session,当更新session最后使用时间时,将list中的session放到列表首部。list首部是最近使用的session。
当清理过期遍历时,从list尾部遍历,直到遇到第一个没有过期的session结束。

map的value中存储的是*list.Element类型,通过map的key获取element,然后通过element操作list。如果map的value中只存了使用方的value类型,则无法通过map的key快速找到list中的元素。

链表的节点存储key和value,使用节点中的key可以操作map。 类似于java中linkedhashmap 的内部类Entry<k,v>

java中 类似需求 可以使用 LinkedHashMap ,将构造函数第三个参数 设为true

参考资料

思考


上一篇下一篇

猜你喜欢

热点阅读