improve gsession/gmap/gtree

This commit is contained in:
John
2019-11-04 21:26:16 +08:00
parent dcb74ee9df
commit e30b2b0732
24 changed files with 366 additions and 125 deletions

View File

@ -0,0 +1,28 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gtime"
"time"
)
func main() {
s := g.Server()
s.SetConfigWithMap(g.Map{
"SessionMaxAge": time.Minute,
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,4 @@
# Redis数据库配置
[redis]
default = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"

View File

@ -0,0 +1,30 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gsession"
"github.com/gogf/gf/os/gtime"
"time"
)
func main() {
s := g.Server()
s.SetConfigWithMap(g.Map{
"SessionMaxAge": 60 * time.Minute,
"SessionStorage": gsession.NewStorageRedisHashTable(g.Redis()),
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,4 @@
# Redis数据库配置
[redis]
default = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"

View File

@ -0,0 +1,30 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gsession"
"github.com/gogf/gf/os/gtime"
"time"
)
func main() {
s := g.Server()
s.SetConfigWithMap(g.Map{
"SessionMaxAge": time.Minute,
"SessionStorage": gsession.NewStorageRedis(g.Redis()),
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
s.SetPort(8199)
s.Run()
}

View File

@ -1,22 +1,17 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"fmt"
"github.com/gogf/gf/container/gmap"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
})
s.BindHandler("/user", func(r *ghttp.Request) {
})
s.BindHandler("/user/:id", func(r *ghttp.Request) {
r.Response.Write(r.GetRouterString("id"))
})
s.EnablePprof()
s.SetPort(3000)
s.Run()
func Test(data *gmap.Map) {
data = gmap.New()
fmt.Println(data)
}
func main() {
var m *gmap.Map
fmt.Println(m)
Test(m)
fmt.Println(m)
}

View File

@ -368,6 +368,13 @@ func (m *AnyAnyMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *AnyAnyMap) Replace(data map[interface{}]interface{}) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) {
m.mu.Lock()

View File

@ -366,6 +366,13 @@ func (m *IntAnyMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *IntAnyMap) Replace(data map[int]interface{}) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) {
m.mu.Lock()

View File

@ -343,6 +343,13 @@ func (m *IntIntMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *IntIntMap) Replace(data map[int]int) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntIntMap) LockFunc(f func(m map[int]int)) {
m.mu.Lock()

View File

@ -344,6 +344,13 @@ func (m *IntStrMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *IntStrMap) Replace(data map[int]string) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntStrMap) LockFunc(f func(m map[int]string)) {
m.mu.Lock()

View File

@ -362,6 +362,13 @@ func (m *StrAnyMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *StrAnyMap) Replace(data map[string]interface{}) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *StrAnyMap) LockFunc(f func(m map[string]interface{})) {
m.mu.Lock()

View File

@ -345,6 +345,13 @@ func (m *StrIntMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *StrIntMap) Replace(data map[string]int) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *StrIntMap) LockFunc(f func(m map[string]int)) {
m.mu.Lock()

View File

@ -346,6 +346,13 @@ func (m *StrStrMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *StrStrMap) Replace(data map[string]string) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *StrStrMap) LockFunc(f func(m map[string]string)) {
m.mu.Lock()

View File

@ -92,6 +92,21 @@ func (m *ListMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *ListMap) Replace(data map[interface{}]interface{}) {
m.mu.Lock()
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
for key, value := range data {
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
e.Value = &gListMapNode{key, value}
}
}
m.mu.Unlock()
}
// Map returns a copy of the data of the map.
func (m *ListMap) Map() map[interface{}]interface{} {
m.mu.RLock()

View File

@ -386,6 +386,17 @@ func (tree *AVLTree) Clear() {
tree.size = 0
}
// Replace the data of the tree with given <data>.
func (tree *AVLTree) Replace(data map[interface{}]interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.root = nil
tree.size = 0
for key, value := range data {
tree.put(key, value, nil, &tree.root)
}
}
// String returns a string representation of container
func (tree *AVLTree) String() string {
tree.mu.RLock()

View File

@ -324,6 +324,17 @@ func (tree *BTree) Clear() {
tree.size = 0
}
// Replace the data of the tree with given <data>.
func (tree *BTree) Replace(data map[interface{}]interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.root = nil
tree.size = 0
for k, v := range data {
tree.doSet(k, v)
}
}
// Height returns the height of the tree.
func (tree *BTree) Height() int {
tree.mu.RLock()

View File

@ -605,6 +605,17 @@ func (tree *RedBlackTree) Clear() {
tree.size = 0
}
// Replace the data of the tree with given <data>.
func (tree *RedBlackTree) Replace(data map[interface{}]interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.root = nil
tree.size = 0
for k, v := range data {
tree.doSet(k, v)
}
}
// String returns a string representation of container.
func (tree *RedBlackTree) String() string {
tree.mu.RLock()

View File

@ -77,6 +77,8 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
}
// access log
s.handleAccessLog(request)
// 关闭当前Session并更新会话超时时间
request.Session.Close()
}()
// ============================================================
@ -168,8 +170,6 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
if !request.IsExited() {
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
}
// 关闭当前Session并更新会话超时时间
request.Session.Close()
}
// 查找静态文件的绝对路径

View File

@ -7,6 +7,7 @@
package gsession
import (
"github.com/gogf/gf/container/gmap"
"time"
"github.com/gogf/gf/os/gcache"
@ -14,16 +15,16 @@ import (
// Manager for sessions.
type Manager struct {
ttl time.Duration // TTL for sessions.
storage Storage // Storage interface for session storage Set/Get.
sessions *gcache.Cache // Session cache for session TTL.
ttl time.Duration // TTL for sessions.
storage Storage // Storage interface for session storage Set/Get.
sessionData *gcache.Cache // Session data cache for session TTL.
}
// New creates and returns a new session manager.
func New(ttl time.Duration, storage ...Storage) *Manager {
m := &Manager{
ttl: ttl,
sessions: gcache.New(),
ttl: ttl,
sessionData: gcache.New(),
}
if len(storage) > 0 && storage[0] != nil {
m.storage = storage[0]
@ -39,12 +40,6 @@ func (m *Manager) New(sessionId ...string) *Session {
if len(sessionId) > 0 && sessionId[0] != "" {
id = sessionId[0]
}
// NOTE:
// We CANNOT creates and stores session directly to manager
// as it might be a fake and invalid session id
// which would consumes your memory as much as possible.
//
// We here create a temporary tiny session struct.
return &Session{
id: id,
manager: m,
@ -67,7 +62,6 @@ func (m *Manager) TTL() time.Duration {
}
// UpdateSessionTTL updates the ttl for given session.
// If this session is dirty, it also exports it to storage.
func (m *Manager) UpdateSessionTTL(id string, session *Session) {
m.sessions.Set(id, session, m.ttl)
func (m *Manager) UpdateSessionTTL(id string, data *gmap.StrAnyMap) {
m.sessionData.Set(id, data, m.ttl)
}

View File

@ -7,10 +7,9 @@
package gsession
import (
"github.com/gogf/gf/internal/intlog"
"time"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/os/gtime"
@ -21,25 +20,33 @@ import (
type Session struct {
id string // Session id.
data *gmap.StrAnyMap // Session data.
dirty *gtype.Bool // Used to mark session is modified.
dirty bool // Used to mark session is modified.
start bool // Used to mark session is started.
manager *Manager // Parent manager.
}
// init does the delay initialization for session.
// It here to initialization real session if necessary.
// It here initializes real session if necessary.
func (s *Session) init() {
if s.dirty == nil {
s.dirty = gtype.NewBool(false)
if s.start {
return
}
if len(s.id) > 0 && s.data == nil {
if data := s.manager.storage.GetSession(s.id, s.manager.ttl); data != nil {
if s.data = gmap.NewStrAnyMapFrom(data, true); s.data == nil {
panic("session restoring failed for id:" + s.id)
if len(s.id) > 0 {
var err error
// Retrieve memory session data from manager.
if r := s.manager.sessionData.Get(s.id); r != nil {
s.data = r.(*gmap.StrAnyMap)
intlog.Print("session init data:", s.data)
}
// Retrieve stored session data from storage.
if s.manager.storage != nil {
if s.data, err = s.manager.storage.GetSession(s.id, s.manager.ttl, s.data); err != nil {
intlog.Errorf("session restoring failed for id '%s': %v", s.id, err)
}
return
} else {
// Invalid or expired session id,
// it should create a new one.
}
// If it's an invalid or expired session id,
// it should create a new session id.
if s.data == nil {
s.id = ""
}
}
@ -52,6 +59,31 @@ func (s *Session) init() {
if s.data == nil {
s.data = gmap.NewStrAnyMap(true)
}
s.start = true
}
// Close closes current session and updates its ttl in the session manager.
// If this session is dirty, it also exports it to storage.
//
// NOTE that this function must be called ever after a session request done.
func (s *Session) Close() {
if s.start && len(s.id) > 0 {
size := s.data.Size()
if s.manager.storage != nil {
if s.dirty {
if err := s.manager.storage.SetSession(s.id, s.data, s.manager.ttl); err != nil {
panic(err)
}
} else if size > 0 {
if err := s.manager.storage.UpdateTTL(s.id, s.manager.ttl); err != nil {
panic(err)
}
}
}
if s.dirty || size > 0 {
s.manager.UpdateSessionTTL(s.id, s.data)
}
}
}
// Set sets key-value pair to this session.
@ -64,7 +96,7 @@ func (s *Session) Set(key string, value interface{}) error {
return err
}
}
s.dirty.Set(true)
s.dirty = true
return nil
}
@ -78,7 +110,7 @@ func (s *Session) Sets(data map[string]interface{}) error {
return err
}
}
s.dirty.Set(true)
s.dirty = true
return nil
}
@ -95,7 +127,7 @@ func (s *Session) Remove(key string) error {
return err
}
}
s.dirty.Set(true)
s.dirty = true
return nil
}
@ -117,7 +149,7 @@ func (s *Session) RemoveAll() error {
return err
}
}
s.dirty.Set(true)
s.dirty = true
return nil
}
@ -161,33 +193,7 @@ func (s *Session) Contains(key string) bool {
// IsDirty checks whether there's any data changes in the session.
func (s *Session) IsDirty() bool {
if s.dirty == nil {
return false
}
return s.dirty.Val()
}
// Close closes current session and updates its ttl in the session manager.
// If this session is dirty, it also exports it to storage.
//
// NOTE that this function must be called ever after a session request done.
func (s *Session) Close() {
if len(s.id) > 0 && s.data != nil {
if s.manager.storage != nil {
if s.dirty.Cas(true, false) {
s.data.RLockFunc(func(m map[string]interface{}) {
if err := s.manager.storage.SetSession(s.id, m, s.manager.ttl); err != nil {
panic(err)
}
})
} else {
if err := s.manager.storage.UpdateTTL(s.id, s.manager.ttl); err != nil {
panic(err)
}
}
}
s.manager.UpdateSessionTTL(s.id, s)
}
return s.dirty
}
// Get retrieves session value with given key.

View File

@ -6,7 +6,11 @@
package gsession
import "time"
import (
"errors"
"github.com/gogf/gf/container/gmap"
"time"
)
type Storage interface {
// New creates a custom session id.
@ -37,17 +41,25 @@ type Storage interface {
// RemoveAll deletes all key-value pairs from storage.
RemoveAll(id string) error
// GetSession returns the session data as map for given session id.
// The parameter <ttl> specifies the TTL for this session.
// It returns nil if the TTL is exceeded.
GetSession(id string, ttl time.Duration) map[string]interface{}
// GetSession returns the session data as *gmap.StrAnyMap for given session id from storage.
//
// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded.
// The parameter <data> is the current old session data stored in memory,
// and for some storage it might be nil if memory storage is disabled.
//
// This function is called ever when session starts.
GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error)
// SetSession updates the data map for specified session id.
// SetSession updates the data for specified session id.
// This function is called ever after session, which is changed dirty, is closed.
// This copy all session data map from memory to storage.
SetSession(id string, data map[string]interface{}, ttl time.Duration) error
SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error
// UpdateTTL updates the TTL for specified session id.
// This function is called ever after session, which is not dirty, is closed.
UpdateTTL(id string, ttl time.Duration) error
}
var (
ErrorDisabled = errors.New("this feature is disabled in this storage")
)

View File

@ -8,7 +8,7 @@ package gsession
import (
"encoding/json"
"errors"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/internal/intlog"
"os"
"time"
@ -40,7 +40,6 @@ var (
DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!")
DefaultStorageFileCryptoEnabled = false
DefaultStorageFileLoopInterval = time.Minute
ErrorDisabled = errors.New("this feature is disabled in this storage")
)
func init() {
@ -151,39 +150,51 @@ func (s *StorageFile) RemoveAll(id string) error {
return ErrorDisabled
}
// GetSession returns the session data as map for given session id.
// The parameter <ttl> specifies the TTL for this session.
// It returns nil if the TTL is exceeded.
func (s *StorageFile) GetSession(id string, ttl time.Duration) map[string]interface{} {
// GetSession returns the session data as *gmap.StrAnyMap for given session id from storage.
//
// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded.
// The parameter <data> is the current old session data stored in memory,
// and for some storage it might be nil if memory storage is disabled.
//
// This function is called ever when session starts.
func (s *StorageFile) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) {
if data != nil {
return data, nil
}
intlog.Printf("StorageFile.GetSession: %s, %v", id, ttl)
path := s.sessionFilePath(id)
data := gfile.GetBytes(path)
if len(data) > 8 {
timestampMilli := gbinary.DecodeToInt64(data[:8])
content := gfile.GetBytes(path)
if len(content) > 8 {
timestampMilli := gbinary.DecodeToInt64(content[:8])
if timestampMilli+ttl.Nanoseconds()/1e6 < gtime.Millisecond() {
return nil
return nil, nil
}
var err error
content := data[8:]
content = content[8:]
// Decrypt with AES.
if s.cryptoEnabled {
content, err = gaes.Decrypt(data[8:], DefaultStorageFileCryptoKey)
content, err = gaes.Decrypt(content, DefaultStorageFileCryptoKey)
if err != nil {
return nil
return nil, err
}
}
var m map[string]interface{}
if err = json.Unmarshal(content, &m); err != nil {
return nil
return nil, err
}
return m
if m == nil {
return nil, nil
}
return gmap.NewStrAnyMapFrom(m, true), nil
}
return nil
return nil, nil
}
// SetSession updates the data map for specified session id.
// This function is called ever after session, which is changed dirty, is closed.
// This copy all session data map from memory to storage.
func (s *StorageFile) SetSession(id string, data map[string]interface{}, ttl time.Duration) error {
func (s *StorageFile) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error {
intlog.Printf("StorageFile.SetSession: %s, %v, %v", id, data, ttl)
path := s.sessionFilePath(id)
content, err := json.Marshal(data)
if err != nil {
@ -214,6 +225,7 @@ func (s *StorageFile) SetSession(id string, data map[string]interface{}, ttl tim
// This function is called ever after session, which is not dirty, is closed.
// It just adds the session id to the async handling queue.
func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error {
intlog.Printf("StorageFile.UpdateTTL: %s, %v", id, ttl)
if ttl >= DefaultStorageRedisLoopInterval {
s.updatingIdSet.Add(id)
}
@ -222,7 +234,7 @@ func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error {
// doUpdateTTL updates the TTL for session id.
func (s *StorageFile) doUpdateTTL(id string) error {
intlog.Printf("update StorageFile TTL for session id: %s", id)
intlog.Printf("StorageFile.doUpdateTTL: %s", id)
path := s.sessionFilePath(id)
file, err := gfile.OpenWithFlag(path, os.O_WRONLY)
if err != nil {

View File

@ -32,6 +32,7 @@ var (
// NewStorageRedis creates and returns a redis storage object for session.
func NewStorageRedis(redis *gredis.Redis, prefix ...string) *StorageRedis {
if redis == nil {
panic("redis instance for storage cannot be empty")
return nil
}
s := &StorageRedis{
@ -105,26 +106,43 @@ func (s *StorageRedis) RemoveAll(id string) error {
return ErrorDisabled
}
// GetSession returns the session data as map for given session id.
// The parameter <ttl> specifies the TTL for this session.
// It returns nil if the TTL is exceeded.
func (s *StorageRedis) GetSession(id string, ttl time.Duration) map[string]interface{} {
// GetSession returns the session data as *gmap.StrAnyMap for given session id from storage.
//
// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded.
// The parameter <data> is the current old session data stored in memory,
// and for some storage it might be nil if memory storage is disabled.
//
// This function is called ever when session starts.
func (s *StorageRedis) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) {
intlog.Printf("StorageRedis.GetSession: %s, %v", id, ttl)
r, err := s.redis.DoVar("GET", s.key(id))
if err != nil {
// If it does not exist, it returns an empty map to replace the session memory data.
return map[string]interface{}{}
return nil, err
}
content := r.Bytes()
if len(content) == 0 {
return nil, nil
}
var m map[string]interface{}
if err = json.Unmarshal(r.Bytes(), &m); err != nil {
return nil
if err = json.Unmarshal(content, &m); err != nil {
return nil, err
}
return m
if m == nil {
return nil, nil
}
if data == nil {
return gmap.NewStrAnyMapFrom(m, true), nil
} else {
data.Replace(m)
}
return data, nil
}
// SetSession updates the data map for specified session id.
// This function is called ever after session, which is changed dirty, is closed.
// This copy all session data map from memory to storage.
func (s *StorageRedis) SetSession(id string, data map[string]interface{}, ttl time.Duration) error {
func (s *StorageRedis) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error {
intlog.Printf("StorageRedis.SetSession: %s, %v, %v", id, data, ttl)
content, err := json.Marshal(data)
if err != nil {
return err
@ -137,6 +155,7 @@ func (s *StorageRedis) SetSession(id string, data map[string]interface{}, ttl ti
// This function is called ever after session, which is not dirty, is closed.
// It just adds the session id to the async handling queue.
func (s *StorageRedis) UpdateTTL(id string, ttl time.Duration) error {
intlog.Printf("StorageRedis.UpdateTTL: %s, %v", id, ttl)
if ttl >= DefaultStorageRedisLoopInterval {
s.updatingIdMap.Set(id, int(ttl.Seconds()))
}
@ -145,7 +164,7 @@ func (s *StorageRedis) UpdateTTL(id string, ttl time.Duration) error {
// doUpdateTTL updates the TTL for session id.
func (s *StorageRedis) doUpdateTTL(id string, ttlSeconds int) error {
intlog.Printf("update StorageRedis TTL for session id: %s, %d", id, ttlSeconds)
intlog.Printf("StorageRedis.doUpdateTTL: %s, %d", id, ttlSeconds)
_, err := s.redis.DoVar("EXPIRE", s.key(id), ttlSeconds)
return err
}

View File

@ -7,6 +7,7 @@
package gsession
import (
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/database/gredis"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/util/gconv"
@ -22,6 +23,7 @@ type StorageRedisHashTable struct {
// NewStorageRedisHashTable creates and returns a redis hash table storage object for session.
func NewStorageRedisHashTable(redis *gredis.Redis, prefix ...string) *StorageRedisHashTable {
if redis == nil {
panic("redis instance for storage cannot be empty")
return nil
}
s := &StorageRedisHashTable{
@ -42,8 +44,8 @@ func (s *StorageRedisHashTable) New(ttl time.Duration) (id string) {
// Get retrieves session value with given key.
// It returns nil if the key does not exist in the session.
func (s *StorageRedisHashTable) Get(id string, key string) interface{} {
r, _ := s.redis.Do("HGET", s.key(id), key)
return r
r, _ := s.redis.DoVar("HGET", s.key(id), key)
return r.String()
}
// GetMap retrieves all key-value pairs as map from storage.
@ -55,7 +57,7 @@ func (s *StorageRedisHashTable) GetMap(id string) map[string]interface{} {
array := r.Interfaces()
m := make(map[string]interface{})
for i := 0; i < len(array); i += 2 {
m[gconv.String(array[i])] = array[i+1]
m[gconv.String(array[i])] = gconv.String(array[i+1])
}
return m
}
@ -101,21 +103,29 @@ func (s *StorageRedisHashTable) RemoveAll(id string) error {
return err
}
// GetSession returns the session data as map for given session id.
// The parameter <ttl> specifies the TTL for this session.
// It returns nil if the TTL is exceeded.
func (s *StorageRedisHashTable) GetSession(id string, ttl time.Duration) map[string]interface{} {
r, _ := s.redis.DoVar("EXISTS", s.key(id))
if r.Bool() {
return map[string]interface{}{}
// GetSession returns the session data as *gmap.StrAnyMap for given session id from storage.
//
// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded.
// The parameter <data> is the current old session data stored in memory,
// and for some storage it might be nil if memory storage is disabled.
//
// This function is called ever when session starts.
func (s *StorageRedisHashTable) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) {
intlog.Printf("StorageRedisHashTable.GetSession: %s, %v", id, ttl)
r, err := s.redis.DoVar("EXISTS", s.key(id))
if err != nil {
return nil, err
}
return nil
if r.Bool() {
return gmap.NewStrAnyMap(true), nil
}
return nil, nil
}
// SetSession updates the data map for specified session id.
// This function is called ever after session, which is changed dirty, is closed.
// This copy all session data map from memory to storage.
func (s *StorageRedisHashTable) SetSession(id string, data map[string]interface{}, ttl time.Duration) error {
func (s *StorageRedisHashTable) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error {
intlog.Printf("StorageRedisHashTable.SetSession: %s, %v", id, ttl)
_, err := s.redis.Do("EXPIRE", s.key(id), ttl.Seconds())
return err