session

package
v1.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 30, 2026 License: Apache-2.0 Imports: 13 Imported by: 8

README

cosgo/session

HTTP 会话管理。支持内存和 Redis 两种后端,内建心跳淘汰机制。

使用场景

一个 Session 绑定到一次请求上下文,不跨请求共享,也不假设跨 goroutine 并发访问。业务层需保证同一个 session 不会被多个请求同时操作(典型 HTTP 中间件模型)。

存储后端选择

// 内存后端(单机,无持久化)
session.Options.Storage = session.NewMemory(10000)  // 容量

// Redis 后端(分布式)
redisStorage, err := session.NewRedis("localhost:6379", "app")
if err != nil { log.Fatal(err) }
session.Options.Storage = redisStorage

核心流程

登录/创建
s := session.New()
token, err := s.Create("u-123", map[string]any{
    "username": "alice",
    "role":     "admin",
})
if err != nil { /* ... */ }
// 把 token 放 Cookie 返回给客户端
验证
s := session.New()
if err := s.Verify(token); err != nil {
    // ErrorSessionEmpty / Illegal / NotExist / Replaced
    return err
}
role := s.GetString("role")

Verify 的 secret 比较走零分配的常量时间字符串比较(constantTimeStringEqual),防时序侧信道,无 []byte 转换开销。

写入
s.Set("last_login", time.Now())
s.Update(map[string]any{"a": 1, "b": 2})

写入会通过 markDirty 累计脏 key,请求结束时(s.Release())统一 flush 到后端存储。

删除
if err := s.Delete(); err != nil { /* ... */ }
释放(必须)
defer s.Release()  // 常在 HTTP 中间件中以 defer 形式调用

Release 做两件事:

  1. 把累计的 dirty key flush 到 Storage.Update
  2. 清空 session 的内部引用,让 Data 可以被 GC

忘记调用会导致修改丢失。

配置

session.Options.Name      = "_my_sid"    // Cookie 名称
session.Options.MaxAge    = 3600         // 秒,0 = 不过期
session.Options.Heartbeat = 10           // 内存后端淘汰心跳周期(秒)

Storage 接口

type Storage interface {
    New(data *Data) error
    Get(id string) (*Data, error)
    Create(uuid string, value map[string]any) (*Data, error)
    Update(data *Data, value map[string]any) error
    Delete(data *Data) error
}

实现自定义后端实现此接口,赋值给 session.Options.Storage 即可。

事件钩子

session.On(session.EventSessionNew,     func(v any) { /* *Data */ })
session.On(session.EventSessionCreated, func(v any) { /* *Data */ })
session.On(session.EventSessionRelease, func(v any) { /* *Data */ })
session.On(session.EventHeartbeat,      func(v any) { /* int32 */ })

事件系统走 copy-on-write + atomic.Pointer:

  • 写路径(On):拷贝整张 listener map,原子发布新版本
  • 读路径(Emit):一次 atomic load,零锁,纳秒级

详见 events.go

并发语义

  • Data 结构:读操作(Get / GetString / Range 等)无锁直接访问 values 字段;写操作(Set / Update / Delete)sync.Mutex + Copy-on-Write,旧快照的读者不受影响。
  • Session 结构:绑定单次请求,调用方不应跨 goroutine 共享。
  • Data.heartbeat:心跳协程每 N 秒 += N,请求协程随时归零。故意不使用 atomic——x86-64 上 aligned int32 读写是单指令完成的,最坏丢失一次累加/归零(差 ±1 个心跳周期),对粗粒度掉线判断无实际影响。
  • Data.Atomic():自增包序列号,atomic.AddInt32 + 双 check 重置,越界安全。

心跳淘汰(内存后端)

Options.Heartbeat > 0 && Options.MaxAge > 0 时启用。

每 Heartbeat 秒触发一次:
  1. Range 所有 Data,累加 heartbeat 值
  2. heartbeat >= MaxAge 的收集到本地 slice
  3. Range 返回后,逐个 Storage.Delete + Emit(EventSessionRelease)

关键:先 Range 收集再 Delete,避免迭代中修改底层 storage。

Redis 后端

  • 键格式:<prefix>-<uuid>(默认 prefix "cookie")
  • 数据以 Redis Hash 存储,HMSet 写入,HGetAll 读取
  • 过期时间由 Options.MaxAge 控制,Create/Get(续约)/Update 时都会刷 Expire
  • Expire 调用失败会 logger.Alert 记录(不会静默吞掉)

安全

处理
Token 比较 constantTimeStringEqual 零分配常量时间比较,防时序侧信道
会话 ID 随机源 内存后端: crypto/rand 生成 28 字符 hex token(bucket+slot+8 字节随机,详见 cosgo/storage);Redis 后端: 用户 UUID
Session 劫持防护 Token = secret(6 字符随机) + sessionID,secret 存在 Data 内部,其它设备登录后 secret 刷新,旧 token 校验返回 ErrorSessionReplaced

常见问题

现象 原因 处理
Verify 返回 ErrorSessionReplaced 其它设备/浏览器登录生成新 token 覆盖了旧 session 引导客户端重新登录
Session 数据丢失 请求结束未调用 Release() / Redis Expire 失败 / 超过 MaxAge 检查 defer / 观察 logger.Alert 日志
Redis 连接失败 Redis 未启动 / 网络 / 密码 检查 NewRedis 返回的 error 并重试

目录结构

session/
├── session.go        Session 入口与生命周期
├── data.go           Data 结构与读写操作
├── data_setter.go    Setter 模板
├── data_getter.go    Getter 模板
├── storage.go        Storage 接口
├── memory.go         内存后端 + 心跳
├── memory_setter.go  内存后端的 Setter 适配
├── redis.go          Redis 后端
├── heartbeat.go      心跳定时器
├── events.go         事件订阅(CoW + atomic.Pointer)
├── options.go        全局配置
├── errors.go         错误常量
└── README.md

Documentation

Overview

Package session 提供会话管理功能,支持内存和Redis存储

Package session 提供会话管理功能,支持内存和Redis存储

Package session 提供会话管理功能,支持内存和Redis存储

Package session 提供会话管理功能,支持内存和Redis存储

Index

Constants

View Source
const ContextRandomStringLength = 6

ContextRandomStringLength Token 前缀随机串长度

View Source
const TokenSecretName = "_TS_"

Variables

View Source
var (
	ErrorStorageEmpty     = values.Errorf(202, "session Storage not set")
	ErrorSessionEmpty     = values.Errorf(203, "session token empty")
	ErrorSessionNotCreate = values.Errorf(204, "session not create")
	ErrorSessionNotExist  = values.Errorf(205, "session not exist")
	ErrorSessionExpired   = values.Errorf(206, "session expire")
	ErrorSessionIllegal   = values.Errorf(207, "session illegal")
	ErrorSessionUnknown   = values.Errorf(208, "session unknown error")
	ErrorSessionReplaced  = values.Errorf(209, "session replaced")
)
View Source
var Heartbeat = heartbeat{}

Heartbeat 全局心跳管理器,只需启动一次

View Source
var MaxDataIndex = int32(math.MaxInt32 - 1000)
View Source
var Options = struct {
	Name      string  // session cookie name
	MaxAge    int64   // 有效期(秒),0 = 不过期
	Storage   Storage // 存储后端
	Heartbeat int32   // 心跳间隔(秒),用于内存后端自动清理过期会话
}{
	Name:      "_cookie_vars",
	MaxAge:    3600,
	Heartbeat: 10,
}

Options 全局配置

Functions

func Emit added in v1.6.8

func Emit(event Event, value any)

Emit 触发事件,无锁读取当前快照。

func Errorf added in v1.6.0

func Errorf(format any, args ...any) error

func Listen added in v1.6.8

func Listen(event Event, listener Listener)

Listen 是 On 的别名。

func NewMemorySetter added in v1.6.0

func NewMemorySetter(id string, data any) storage.Setter

NewMemorySetter 内存后端的 Setter 工厂 将传入的 data 适配为 *Data 并重置 id 为 storage 分配的 token

func On added in v1.6.8

func On(event Event, listener Listener)

On 注册事件监听器。写路径:拷贝旧 map,为目标事件创建全新 slice 并追加,再原子发布。

Types

type Data

type Data struct {
	// contains filtered or unexported fields
}

Data 用户登录信息,不要直接修改 Player.Values 信息

func NewData

func NewData(uuid string, vs map[string]any) *Data

func (*Data) Atomic

func (this *Data) Atomic() int32

Atomic 生成一个自增的包序列号

func (*Data) Delete added in v1.4.0

func (this *Data) Delete(key string, done ...func())

func (*Data) Get added in v1.6.8

func (this *Data) Get(key string) any

Get 获取指定键的值

func (*Data) GetFloat64 added in v1.6.8

func (this *Data) GetFloat64(key string) float64

GetFloat64 获取指定键的浮点数值

func (*Data) GetInt added in v1.6.8

func (this *Data) GetInt(key string) int

GetInt 获取指定键的整数值

func (*Data) GetInt32 added in v1.6.8

func (this *Data) GetInt32(key string) int32

func (*Data) GetInt64 added in v1.6.8

func (this *Data) GetInt64(key string) int64

GetInt64 获取指定键的64位整数值

func (*Data) GetString added in v1.6.8

func (this *Data) GetString(key string) string

GetString 获取指定键的字符串值

func (*Data) Heartbeat

func (this *Data) Heartbeat(v ...int32) int32

func (*Data) Id

func (this *Data) Id() string

func (*Data) Index

func (this *Data) Index() int32

func (*Data) Is

func (this *Data) Is(v *Data) bool

func (*Data) KeepAlive

func (this *Data) KeepAlive()

func (*Data) Mutex added in v1.6.0

func (this *Data) Mutex(cb func(Setter))

Mutex 获得写权限,注意必须使用Setter进行操作

func (*Data) Range added in v1.6.8

func (this *Data) Range(cb func(key string, value any) bool)

Range 遍历所有键值对

func (*Data) Reset

func (this *Data) Reset()

func (*Data) Set

func (this *Data) Set(key string, value any, done ...func()) any

Set 设置Cookie信息 done 可选参数,用于在设置完成后锁内安全执行额外操作

func (*Data) UUID

func (this *Data) UUID() string

func (*Data) Update

func (this *Data) Update(data map[string]any, done ...func())

Update 批量设置Cookie信息 done 可选参数,用于在设置完成后锁内安全执行额外操作

func (*Data) Values added in v1.6.8

func (this *Data) Values() values.Values

type Event added in v1.6.8

type Event int8
const (
	EventSessionNew     Event = iota //SESSION New,参数 *Data
	EventSessionCreated              //SESSION Create时,参数 *Data
	EventSessionRelease              //销毁SESSION时,参数 *Data
	EventHeartbeat                   //心跳,参数 心跳间隔 int32
)

type Listener added in v1.6.8

type Listener func(any)

type Memory

type Memory struct {
	storage.Storage
}

func NewMemory

func NewMemory(cap ...int) *Memory

func (*Memory) Create

func (this *Memory) Create(uuid string, data map[string]any) (p *Data, err error)

Create 创建新SESSION

func (*Memory) Delete

func (this *Memory) Delete(d *Data) error

func (*Memory) Get added in v1.5.12

func (this *Memory) Get(id string) (data *Data, err error)

func (*Memory) Heartbeat added in v1.4.0

func (this *Memory) Heartbeat(i any)

Heartbeat 定时清扫过期 session。 刻意"先收集后删除": Range 回调只把过期 *Data 积攒到本地 slice, Range 返回后再逐个 Storage.Delete,避免迭代中修改底层存储。

func (*Memory) New

func (this *Memory) New(p *Data) error

func (*Memory) Update

func (this *Memory) Update(p *Data, data map[string]any) (err error)

Update 更新信息,内存没事,共享Player信息已经更新过,仅仅设置过期时间 内存模式 data已经更新过,不需要再次更新

type Redis

type Redis struct {
	// contains filtered or unexported fields
}

func NewRedis

func NewRedis(address any, prefix ...string) (c *Redis, err error)

func (*Redis) Create

func (this *Redis) Create(uuid string, data map[string]any) (p *Data, err error)

Create ttl过期时间(s)

func (*Redis) Delete

func (this *Redis) Delete(p *Data) (err error)

func (*Redis) Get added in v1.5.12

func (this *Redis) Get(uuid string) (p *Data, err error)

Get 获取session镜像数据

func (*Redis) New

func (this *Redis) New(p *Data) error

func (*Redis) Update

func (this *Redis) Update(p *Data, data map[string]any) (err error)

type Session

type Session struct {
	*Data
	// contains filtered or unexported fields
}

func New

func New(d ...*Data) *Session

func (*Session) Create

func (this *Session) Create(uuid string, data map[string]any) (token string, err error)

Create 创建SESSION,uuid 用户唯一ID,可以检测是不是重复登录

func (*Session) Delete

func (this *Session) Delete() (err error)

func (*Session) New added in v1.5.12

func (this *Session) New(data *Data) (token string, err error)

func (*Session) Refresh added in v1.6.0

func (this *Session) Refresh() (string, error)

func (*Session) Release

func (this *Session) Release()

Release 释放 session 由HTTP SERVER 自动调用

func (*Session) Set

func (this *Session) Set(key string, val any)

func (*Session) Token added in v1.5.12

func (this *Session) Token() (string, error)

Token 获取当前TOKEN,可能为空

func (*Session) Update

func (this *Session) Update(vs map[string]any)

func (*Session) Verify

func (this *Session) Verify(token string) (err error)

Verify 验证TOKEN信息是否有效,并初始化session

type Setter

type Setter struct {
	*Data
}

func (*Setter) Delete added in v1.6.0

func (this *Setter) Delete(key string)

func (*Setter) Set

func (this *Setter) Set(key string, value any) any

func (*Setter) Update added in v1.6.0

func (this *Setter) Update(data map[string]any)

type Storage

type Storage interface {
	New(data *Data) error
	Get(id string) (data *Data, err error)
	Create(uuid string, value map[string]any) (data *Data, err error)
	Update(data *Data, value map[string]any) error
	Delete(data *Data) error
}

Storage 存储接口

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL