From bd4d273e1c44499ca4f3984779f84ed3901c5198 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 28 Jul 2019 11:50:12 +0800 Subject: [PATCH 1/3] improve ghttp for session --- g/net/ghttp/ghttp_server_config.go | 7 +- g/net/ghttp/ghttp_server_session.go | 66 ++++++++++++++-- g/util/gconv/gconv_slice.go | 20 ++--- geg/net/ghttp/server/session/config.toml | 2 + geg/net/ghttp/server/session/session.go | 2 +- geg/net/ghttp/server/session/session_redis.go | 78 +++++++++++++++++++ 6 files changed, 155 insertions(+), 20 deletions(-) create mode 100644 geg/net/ghttp/server/session/config.toml create mode 100644 geg/net/ghttp/server/session/session_redis.go diff --git a/g/net/ghttp/ghttp_server_config.go b/g/net/ghttp/ghttp_server_config.go index 0a5b31e90..cb8fee3b5 100644 --- a/g/net/ghttp/ghttp_server_config.go +++ b/g/net/ghttp/ghttp_server_config.go @@ -9,11 +9,12 @@ package ghttp import ( "crypto/tls" "fmt" - "github.com/gogf/gf/g/os/gfile" - "github.com/gogf/gf/g/os/glog" "net/http" "strconv" "time" + + "github.com/gogf/gf/g/os/gfile" + "github.com/gogf/gf/g/os/glog" ) const ( @@ -25,7 +26,7 @@ const ( NAME_TO_URI_TYPE_CAMEL = 3 // 采用驼峰命名方式 gDEFAULT_COOKIE_PATH = "/" // 默认path gDEFAULT_COOKIE_MAX_AGE = 86400 * 365 // 默认cookie有效期(一年) - gDEFAULT_SESSION_MAX_AGE = 600000 // 默认session有效期(600秒) + gDEFAULT_SESSION_MAX_AGE = 86400 // 默认session有效期(一天) gDEFAULT_SESSION_ID_NAME = "gfsessionid" // 默认存放Cookie中的SessionId名称 gCHANGE_CONFIG_WHILE_RUNNING_ERROR = "cannot be changed while running" ) diff --git a/g/net/ghttp/ghttp_server_session.go b/g/net/ghttp/ghttp_server_session.go index 01fc03dd5..6a2411ae0 100644 --- a/g/net/ghttp/ghttp_server_session.go +++ b/g/net/ghttp/ghttp_server_session.go @@ -8,14 +8,16 @@ package ghttp import ( + "encoding/json" + "strconv" + "strings" + "time" + "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gvar" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "strconv" - "strings" - "time" ) // SESSION对象 @@ -62,13 +64,18 @@ func (s *Session) init() { } } +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (s *Session) MarshalJSON() ([]byte, error) { + return json.Marshal(s.data) +} + // 获取/创建SessionId func (s *Session) Id() string { s.init() return s.id } -// 获取当前session所有数据 +// 获取当前session所有数据,注意是值拷贝 func (s *Session) Map() map[string]interface{} { if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { s.init() @@ -77,6 +84,15 @@ func (s *Session) Map() map[string]interface{} { return nil } +// 获得session map大小 +func (s *Session) Size() int { + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + return s.data.Size() + } + return 0 +} + // 设置session func (s *Session) Set(key string, value interface{}) { s.init() @@ -117,7 +133,7 @@ func (s *Session) GetVar(key string, def ...interface{}) *gvar.Var { return gvar.New(s.Get(key, def...), true) } -// 删除session +// 删除指定session键值对 func (s *Session) Remove(key string) { if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { s.init() @@ -125,6 +141,17 @@ func (s *Session) Remove(key string) { } } +// 从json字符串中恢复session数据 +func (s *Session) RestoreFromJson(data []byte) (err error) { + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + s.data.LockFunc(func(m map[string]interface{}) { + err = json.Unmarshal(data, &m) + }) + } + return +} + // 清空session func (s *Session) Clear() { if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { @@ -228,7 +255,34 @@ func (s *Session) GetDuration(key string, def ...interface{}) time.Duration { return gconv.Duration(s.Get(key, def...)) } -// 将变量转换为对象,注意 pointer 参数必须为struct指针 +func (s *Session) GetMap(value interface{}, tags ...string) map[string]interface{} { + return gconv.Map(value, tags...) +} + +func (s *Session) GetMapDeep(value interface{}, tags ...string) map[string]interface{} { + return gconv.MapDeep(value, tags...) +} + +func (s *Session) GetMaps(value interface{}, tags ...string) []map[string]interface{} { + return gconv.Maps(value, tags...) +} + +func (s *Session) GetMapsDeep(value interface{}, tags ...string) []map[string]interface{} { + return gconv.MapsDeep(value, tags...) +} + func (s *Session) GetStruct(key string, pointer interface{}, mapping ...map[string]string) error { return gconv.Struct(s.Get(key), pointer, mapping...) } + +func (s *Session) GetStructDeep(key string, pointer interface{}, mapping ...map[string]string) error { + return gconv.StructDeep(s.Get(key), pointer, mapping...) +} + +func (s *Session) GetStructs(key string, pointer interface{}, mapping ...map[string]string) error { + return gconv.Structs(s.Get(key), pointer, mapping...) +} + +func (s *Session) GetStructsDeep(key string, pointer interface{}, mapping ...map[string]string) error { + return gconv.StructsDeep(s.Get(key), pointer, mapping...) +} diff --git a/g/util/gconv/gconv_slice.go b/g/util/gconv/gconv_slice.go index 0592e5362..137754333 100644 --- a/g/util/gconv/gconv_slice.go +++ b/g/util/gconv/gconv_slice.go @@ -379,40 +379,40 @@ func Interfaces(i interface{}) []interface{} { } // Maps converts to []map[string]interface{}. -func Maps(i interface{}) []map[string]interface{} { - if i == nil { +func Maps(value interface{}, tags ...string) []map[string]interface{} { + if value == nil { return nil } - if r, ok := i.([]map[string]interface{}); ok { + if r, ok := value.([]map[string]interface{}); ok { return r } else { - array := Interfaces(i) + array := Interfaces(value) if len(array) == 0 { return nil } list := make([]map[string]interface{}, len(array)) for k, v := range array { - list[k] = Map(v) + list[k] = Map(v, tags...) } return list } } // MapsDeep converts to []map[string]interface{} recursively. -func MapsDeep(i interface{}) []map[string]interface{} { - if i == nil { +func MapsDeep(value interface{}, tags ...string) []map[string]interface{} { + if value == nil { return nil } - if r, ok := i.([]map[string]interface{}); ok { + if r, ok := value.([]map[string]interface{}); ok { return r } else { - array := Interfaces(i) + array := Interfaces(value) if len(array) == 0 { return nil } list := make([]map[string]interface{}, len(array)) for k, v := range array { - list[k] = MapDeep(v) + list[k] = MapDeep(v, tags...) } return list } diff --git a/geg/net/ghttp/server/session/config.toml b/geg/net/ghttp/server/session/config.toml new file mode 100644 index 000000000..d5195ad6d --- /dev/null +++ b/geg/net/ghttp/server/session/config.toml @@ -0,0 +1,2 @@ +[redis] + default = "127.0.0.1:6379,10" \ No newline at end of file diff --git a/geg/net/ghttp/server/session/session.go b/geg/net/ghttp/server/session/session.go index b7893bd56..3d901fdd3 100644 --- a/geg/net/ghttp/server/session/session.go +++ b/geg/net/ghttp/server/session/session.go @@ -22,7 +22,7 @@ func (c *Controller) DoLogin() { } func (c *Controller) Main() { - c.Response.WriteJson(c.Session.Data()) + c.Response.WriteJson(c.Session.Map()) } func main() { diff --git a/geg/net/ghttp/server/session/session_redis.go b/geg/net/ghttp/server/session/session_redis.go new file mode 100644 index 000000000..525f43b8e --- /dev/null +++ b/geg/net/ghttp/server/session/session_redis.go @@ -0,0 +1,78 @@ +package main + +import ( + "encoding/json" + + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" + "github.com/gogf/gf/g/os/gtime" +) + +// 测试,SESSION写入 +func SessionSet(r *ghttp.Request) { + r.Session.Set("time", gtime.Second()) + r.Response.WriteJson("ok") +} + +// 测试,SESSION读取 +func SessionGet(r *ghttp.Request) { + r.Response.WriteJson(r.Session.Map()) +} + +// 请求处理之前将Redis中的数据读取出来并存储到SESSION对象中。 +func RedisHandlerGet(r *ghttp.Request) { + if !r.IsFileRequest() { + sessionId := r.Cookie.GetSessionId() + if sessionId == "" { + return + } + value, err := g.Redis().DoVar("GET", sessionId) + if err != nil { + panic(err) + } + if !value.IsNil() { + err := r.Session.RestoreFromJson(value.Bytes()) + if err != nil { + panic(err) + } + } + } +} + +// 请求结束时将SESSION数据存储到Redis中,或者在SESSION删除时也删除Redis中的数据。 +func RedisHandlerSet(r *ghttp.Request) { + if !r.IsFileRequest() { + sessionId := r.Cookie.GetSessionId() + if sessionId == "" { + return + } + err := (error)(nil) + if r.Session.Size() > 0 { + value, err := json.Marshal(r.Session) + if err != nil { + panic(err) + } + _, err = g.Redis().Do("SETEX", r.Cookie.GetSessionId(), r.Server.GetSessionMaxAge(), value) + if err != nil { + panic(err) + } + } else { + _, err = g.Redis().Do("DEL", r.Cookie.GetSessionId()) + if err != nil { + panic(err) + } + } + } +} + +func main() { + s := g.Server() + s.BindHandler("/set", SessionSet) + s.BindHandler("/get", SessionGet) + s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{ + ghttp.HOOK_BEFORE_SERVE: RedisHandlerGet, + ghttp.HOOK_AFTER_SERVE: RedisHandlerSet, + }) + s.SetPort(8199) + s.Run() +} From a36e00efeb0d60175328bd80e38f96411ccba2e4 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 28 Jul 2019 13:10:34 +0800 Subject: [PATCH 2/3] improve ghttp for session storage with redis server --- g/net/ghttp/ghttp_server_session.go | 111 +++++++++------- g/os/gcache/gcache_z_bench_test.go | 120 +++++++----------- geg/net/ghttp/server/session/session_redis.go | 47 ++++--- version.go | 2 +- 4 files changed, 133 insertions(+), 147 deletions(-) diff --git a/g/net/ghttp/ghttp_server_session.go b/g/net/ghttp/ghttp_server_session.go index 6a2411ae0..68a9c2264 100644 --- a/g/net/ghttp/ghttp_server_session.go +++ b/g/net/ghttp/ghttp_server_session.go @@ -1,9 +1,8 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// 并发安全的Session管理器 package ghttp @@ -20,17 +19,18 @@ import ( "github.com/gogf/gf/g/util/grand" ) -// SESSION对象 +// SESSION对象,并发安全 type Session struct { id string // SessionId data *gmap.StrAnyMap // Session数据 + dirty bool // 数据是否被修改 server *Server // 所属Server request *Request // 关联的请求 } // 生成一个唯一的SessionId字符串,长度18位。 func makeSessionId() string { - return strings.ToUpper(strconv.FormatInt(gtime.Nanosecond(), 36) + grand.RandStr(6)) + return strings.ToUpper(strconv.FormatInt(gtime.Nanosecond(), 36) + grand.Str(6)) } // 获取或者生成一个session对象(延迟初始化) @@ -43,15 +43,12 @@ func GetSession(r *Request) *Session { } } -// 执行初始化(用于延迟初始化). +// 延迟初始化 func (s *Session) init() { if len(s.id) == 0 { s.server = s.request.Server - // 根据提交的SESSION ID获取已存在SESSION - id := s.request.Cookie.GetSessionId() - if id != "" { - data := s.server.sessions.Get(id) - if data != nil { + if id := s.request.Cookie.GetSessionId(); id != "" { + if data := s.server.sessions.Get(id); data != nil { s.id = id s.data = data.(*gmap.StrAnyMap) return @@ -61,14 +58,10 @@ func (s *Session) init() { s.id = s.request.Cookie.MakeSessionId() s.data = gmap.NewStrAnyMap() s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000) + s.dirty = true } } -// MarshalJSON implements the interface MarshalJSON for json.Marshal. -func (s *Session) MarshalJSON() ([]byte, error) { - return json.Marshal(s.data) -} - // 获取/创建SessionId func (s *Session) Id() string { s.init() @@ -97,12 +90,14 @@ func (s *Session) Size() int { func (s *Session) Set(key string, value interface{}) { s.init() s.data.Set(key, value) + s.dirty = true } // 批量设置 func (s *Session) Sets(m map[string]interface{}) { s.init() s.data.Sets(m) + s.dirty = true } // 判断键名是否存在 @@ -114,6 +109,58 @@ func (s *Session) Contains(key string) bool { return false } +// 判断session是否有修改(包括新创建) +func (s *Session) IsDirty() bool { + return s.dirty +} + +// 删除指定session键值对 +func (s *Session) Remove(key string) { + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + s.data.Remove(key) + s.dirty = true + } +} + +// 将session数据导出为[]byte数据(目前使用json进行序列化) +func (s *Session) Export() (data []byte, err error) { + if s.Size() > 0 { + data, err = json.Marshal(s.data) + } + return +} + +// 从[]byte中恢复session数据(目前使用json进行序列化) +func (s *Session) Restore(data []byte) (err error) { + if len(data) == 0 { + return nil + } + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + s.data.LockFunc(func(m map[string]interface{}) { + err = json.Unmarshal(data, &m) + }) + } + return +} + +// 清空session +func (s *Session) Clear() { + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + s.data.Clear() + s.dirty = true + } +} + +// 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除) +func (s *Session) UpdateExpire() { + if len(s.id) > 0 && s.data.Size() > 0 { + s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000) + } +} + // 获取SESSION变量 func (s *Session) Get(key string, def ...interface{}) interface{} { if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { @@ -133,40 +180,6 @@ func (s *Session) GetVar(key string, def ...interface{}) *gvar.Var { return gvar.New(s.Get(key, def...), true) } -// 删除指定session键值对 -func (s *Session) Remove(key string) { - if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { - s.init() - s.data.Remove(key) - } -} - -// 从json字符串中恢复session数据 -func (s *Session) RestoreFromJson(data []byte) (err error) { - if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { - s.init() - s.data.LockFunc(func(m map[string]interface{}) { - err = json.Unmarshal(data, &m) - }) - } - return -} - -// 清空session -func (s *Session) Clear() { - if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { - s.init() - s.data.Clear() - } -} - -// 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除) -func (s *Session) UpdateExpire() { - if len(s.id) > 0 && s.data.Size() > 0 { - s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000) - } -} - func (s *Session) GetString(key string, def ...interface{}) string { return gconv.String(s.Get(key, def...)) } diff --git a/g/os/gcache/gcache_z_bench_test.go b/g/os/gcache/gcache_z_bench_test.go index ffef27c30..066585624 100644 --- a/g/os/gcache/gcache_z_bench_test.go +++ b/g/os/gcache/gcache_z_bench_test.go @@ -10,104 +10,70 @@ package gcache_test import ( "github.com/gogf/gf/g/os/gcache" - "sync" "testing" ) var ( - c = gcache.New() - clru = gcache.New(10000) - mInt = make(map[int]int) - mMap = make(map[interface{}]interface{}) - - muInt = sync.RWMutex{} - muMap = sync.RWMutex{} + cache = gcache.New() + cacheLru = gcache.New(10000) ) func Benchmark_CacheSet(b *testing.B) { - for i := 0; i < b.N; i++ { - c.Set(i, i, 0) - } + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + cache.Set(i, i, 0) + i++ + } + }) } func Benchmark_CacheGet(b *testing.B) { - for i := 0; i < b.N; i++ { - c.Get(i) - } + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + cache.Get(i) + i++ + } + }) } func Benchmark_CacheRemove(b *testing.B) { - for i := 0; i < b.N; i++ { - c.Remove(i) - } + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + cache.Remove(i) + i++ + } + }) } func Benchmark_CacheLruSet(b *testing.B) { - for i := 0; i < b.N; i++ { - clru.Set(i, i, 0) - } + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + cacheLru.Set(i, i, 0) + i++ + } + }) } func Benchmark_CacheLruGet(b *testing.B) { - for i := 0; i < b.N; i++ { - clru.Get(i) - } + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + cacheLru.Get(i) + i++ + } + }) } func Benchmark_CacheLruRemove(b *testing.B) { - for i := 0; i < b.N; i++ { - clru.Remove(i) - } -} - -func Benchmark_InterfaceMapWithLockSet(b *testing.B) { - for i := 0; i < b.N; i++ { - muMap.Lock() - mMap[i] = i - muMap.Unlock() - } -} - -func Benchmark_InterfaceMapWithLockGet(b *testing.B) { - for i := 0; i < b.N; i++ { - muMap.RLock() - if _, ok := mMap[i]; ok { - + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + cacheLru.Remove(i) + i++ } - muMap.RUnlock() - } -} - -func Benchmark_InterfaceMapWithLockRemove(b *testing.B) { - for i := 0; i < b.N; i++ { - muMap.Lock() - delete(mMap, i) - muMap.Unlock() - } -} - -func Benchmark_IntMapWithLockWithLockSet(b *testing.B) { - for i := 0; i < b.N; i++ { - muInt.Lock() - mInt[i] = i - muInt.Unlock() - } -} - -func Benchmark_IntMapWithLockGet(b *testing.B) { - for i := 0; i < b.N; i++ { - muInt.RLock() - if _, ok := mInt[i]; ok { - - } - muInt.RUnlock() - } -} - -func Benchmark_IntMapWithLockRemove(b *testing.B) { - for i := 0; i < b.N; i++ { - muInt.Lock() - delete(mInt, i) - muInt.Unlock() - } + }) } diff --git a/geg/net/ghttp/server/session/session_redis.go b/geg/net/ghttp/server/session/session_redis.go index 525f43b8e..bfd6164ce 100644 --- a/geg/net/ghttp/server/session/session_redis.go +++ b/geg/net/ghttp/server/session/session_redis.go @@ -1,8 +1,6 @@ package main import ( - "encoding/json" - "github.com/gogf/gf/g" "github.com/gogf/gf/g/net/ghttp" "github.com/gogf/gf/g/os/gtime" @@ -22,17 +20,21 @@ func SessionGet(r *ghttp.Request) { // 请求处理之前将Redis中的数据读取出来并存储到SESSION对象中。 func RedisHandlerGet(r *ghttp.Request) { if !r.IsFileRequest() { - sessionId := r.Cookie.GetSessionId() - if sessionId == "" { + id := r.Cookie.GetSessionId() + if id == "" { return } - value, err := g.Redis().DoVar("GET", sessionId) + // 当内存中的SESSION存在时不需要读取Redis + if r.Session.Size() > 0 { + return + } + // SESSION不存在时,例如服务重启,自动从Redis读取并恢复数据 + value, err := g.Redis().DoVar("GET", id) if err != nil { panic(err) } if !value.IsNil() { - err := r.Session.RestoreFromJson(value.Bytes()) - if err != nil { + if err := r.Session.Restore(value.Bytes()); err != nil { panic(err) } } @@ -42,25 +44,30 @@ func RedisHandlerGet(r *ghttp.Request) { // 请求结束时将SESSION数据存储到Redis中,或者在SESSION删除时也删除Redis中的数据。 func RedisHandlerSet(r *ghttp.Request) { if !r.IsFileRequest() { - sessionId := r.Cookie.GetSessionId() - if sessionId == "" { + id := r.Cookie.GetSessionId() + if id == "" { return } err := (error)(nil) + value := ([]byte)(nil) if r.Session.Size() > 0 { - value, err := json.Marshal(r.Session) - if err != nil { - panic(err) - } - _, err = g.Redis().Do("SETEX", r.Cookie.GetSessionId(), r.Server.GetSessionMaxAge(), value) - if err != nil { - panic(err) + if value, err = r.Session.Export(); err == nil { + if len(value) == 0 { + return + } else if !r.Session.IsDirty() { + // 更新过期时间 + _, err = g.Redis().Do("EXPIRE", id, r.Server.GetSessionMaxAge()) + } else { + // 更新Redis数据 + _, err = g.Redis().Do("SETEX", id, r.Server.GetSessionMaxAge(), value) + } } } else { - _, err = g.Redis().Do("DEL", r.Cookie.GetSessionId()) - if err != nil { - panic(err) - } + // 清空SESSION后自动删除Redis数据 + _, err = g.Redis().Do("DEL", id) + } + if err != nil { + panic(err) } } } diff --git a/version.go b/version.go index 87865121b..e5fd06629 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.8.2" +const VERSION = "v1.8.3" const AUTHORS = "john" From 165602ae0a93190e05a86b890593ed67aeab1b24 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 28 Jul 2019 17:37:13 +0800 Subject: [PATCH 3/3] improve default instance name handling --- TODO.MD | 5 +++++ g/database/gdb/gdb.go | 2 +- g/database/gredis/gredis.go | 2 +- g/frame/gins/gins.go | 4 ++-- g/net/ghttp/ghttp_server.go | 2 +- g/net/ghttp/ghttp_server_router_group.go | 12 ++++++------ g/net/ghttp/ghttp_server_session.go | 12 ++++++++++-- g/net/gtcp/gtcp_server.go | 2 +- g/net/gudp/gudp_server.go | 2 +- g/os/gcfg/gcfg_instance.go | 2 +- g/os/gview/gview_instance.go | 2 +- geg/net/ghttp/server/session/session_redis.go | 7 ++----- 12 files changed, 32 insertions(+), 22 deletions(-) diff --git a/TODO.MD b/TODO.MD index 357d77c94..2851f5b60 100644 --- a/TODO.MD +++ b/TODO.MD @@ -50,6 +50,11 @@ 1. grpool增加支持阻塞添加任务接口; 1. gdb.Model在链式安全的对象创建中增加sync.Pool的使用; 1. 增加g.Table快捷方法以方便操作数据表,但是得考虑后续模型操作设计,特别是脚手架的模型管理; +1. 改进ghttp分组路由中对hook的支持方式,以便格式与BindHookHandler统一; + + + + # DONE 1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index c9235e012..d2236c0f2 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -170,7 +170,7 @@ var ( // which is DEFAULT_GROUP_NAME in default. func New(name ...string) (db DB, err error) { group := configs.defaultGroup - if len(name) > 0 { + if len(name) > 0 && name[0] != "" { group = name[0] } configs.RLock() diff --git a/g/database/gredis/gredis.go b/g/database/gredis/gredis.go index bac5a85b6..3f5dbdc9d 100644 --- a/g/database/gredis/gredis.go +++ b/g/database/gredis/gredis.go @@ -110,7 +110,7 @@ func New(config Config) *Redis { // it returns a redis instance with default group. func Instance(name ...string) *Redis { group := DEFAULT_GROUP_NAME - if len(name) > 0 { + if len(name) > 0 && name[0] != "" { group = name[0] } v := instances.GetOrSetFuncLock(group, func() interface{} { diff --git a/g/frame/gins/gins.go b/g/frame/gins/gins.go index 1acbf8c14..f5c0221c8 100644 --- a/g/frame/gins/gins.go +++ b/g/frame/gins/gins.go @@ -77,7 +77,7 @@ func Config(name ...string) *gcfg.Config { func Database(name ...string) gdb.DB { config := Config() group := gdb.DEFAULT_GROUP_NAME - if len(name) > 0 { + if len(name) > 0 && name[0] != "" { group = name[0] } key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_DATABASE, group) @@ -214,7 +214,7 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode { func Redis(name ...string) *gredis.Redis { config := Config() group := "default" - if len(name) > 0 { + if len(name) > 0 && name[0] != "" { group = name[0] } key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_REDIS, group) diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index f67e0fc68..419097fd5 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -192,7 +192,7 @@ func serverProcessInit() { // 单例模式,请保证name的唯一性 func GetServer(name ...interface{}) *Server { sname := gDEFAULT_SERVER - if len(name) > 0 { + if len(name) > 0 && name[0] != "" { sname = gconv.String(name[0]) } if s := serverMapping.Get(sname); s != nil { diff --git a/g/net/ghttp/ghttp_server_router_group.go b/g/net/ghttp/ghttp_server_router_group.go index be919e0d0..1b8a2d894 100644 --- a/g/net/ghttp/ghttp_server_router_group.go +++ b/g/net/ghttp/ghttp_server_router_group.go @@ -38,13 +38,13 @@ func (s *Server) Group(prefix ...string) *RouterGroup { // 获取分组路由对象 func (d *Domain) Group(prefix ...string) *RouterGroup { - if len(prefix) > 0 { - return &RouterGroup{ - domain: d, - prefix: prefix[0], - } + group := &RouterGroup{ + domain: d, } - return &RouterGroup{} + if len(prefix) > 0 { + group.prefix = prefix[0] + } + return group } // 执行分组路由批量绑定 diff --git a/g/net/ghttp/ghttp_server_session.go b/g/net/ghttp/ghttp_server_session.go index 68a9c2264..c9838e137 100644 --- a/g/net/ghttp/ghttp_server_session.go +++ b/g/net/ghttp/ghttp_server_session.go @@ -43,14 +43,22 @@ func GetSession(r *Request) *Session { } } +// UpdateSession updates the session with custom map. +func (s *Server) UpdateSession(id string, data map[string]interface{}) { + v := s.sessions.GetOrSetFuncLock(id, func() interface{} { + return gmap.NewStrAnyMap() + }, s.GetSessionMaxAge()*1000) + v.(*gmap.StrAnyMap).Sets(data) +} + // 延迟初始化 func (s *Session) init() { if len(s.id) == 0 { s.server = s.request.Server if id := s.request.Cookie.GetSessionId(); id != "" { - if data := s.server.sessions.Get(id); data != nil { + if v := s.server.sessions.Get(id); v != nil { s.id = id - s.data = data.(*gmap.StrAnyMap) + s.data = v.(*gmap.StrAnyMap) return } } diff --git a/g/net/gtcp/gtcp_server.go b/g/net/gtcp/gtcp_server.go index 1fe42377b..7e2b443a5 100644 --- a/g/net/gtcp/gtcp_server.go +++ b/g/net/gtcp/gtcp_server.go @@ -35,7 +35,7 @@ var serverMapping = gmap.NewStrAnyMap() // The parameter is used to specify the TCP server func GetServer(name ...interface{}) *Server { serverName := gDEFAULT_SERVER - if len(name) > 0 { + if len(name) > 0 && name[0] != "" { serverName = gconv.String(name[0]) } return serverMapping.GetOrSetFuncLock(serverName, func() interface{} { diff --git a/g/net/gudp/gudp_server.go b/g/net/gudp/gudp_server.go index e0d7733e2..47c91d5a2 100644 --- a/g/net/gudp/gudp_server.go +++ b/g/net/gudp/gudp_server.go @@ -32,7 +32,7 @@ var serverMapping = gmap.NewStrAnyMap() // 单例模式,请保证name的唯一性 func GetServer(name ...interface{}) *Server { serverName := gDEFAULT_SERVER - if len(name) > 0 { + if len(name) > 0 && name[0] != "" { serverName = gconv.String(name[0]) } if s := serverMapping.Get(serverName); s != nil { diff --git a/g/os/gcfg/gcfg_instance.go b/g/os/gcfg/gcfg_instance.go index ae7c9f0e8..9f5b7ee09 100644 --- a/g/os/gcfg/gcfg_instance.go +++ b/g/os/gcfg/gcfg_instance.go @@ -24,7 +24,7 @@ var ( // The parameter is the name for the instance. func Instance(name ...string) *Config { key := DEFAULT_GROUP_NAME - if len(name) > 0 { + if len(name) > 0 && name[0] != "" { key = name[0] } return instances.GetOrSetFuncLock(key, func() interface{} { diff --git a/g/os/gview/gview_instance.go b/g/os/gview/gview_instance.go index ebb075096..22b31be34 100644 --- a/g/os/gview/gview_instance.go +++ b/g/os/gview/gview_instance.go @@ -22,7 +22,7 @@ var ( // The parameter is the name for the instance. func Instance(name ...string) *View { key := DEFAULT_INSTANCE_NAME - if len(name) > 0 { + if len(name) > 0 && name[0] != "" { key = name[0] } return instances.GetOrSetFuncLock(key, func() interface{} { diff --git a/geg/net/ghttp/server/session/session_redis.go b/geg/net/ghttp/server/session/session_redis.go index bfd6164ce..b70f64edf 100644 --- a/geg/net/ghttp/server/session/session_redis.go +++ b/geg/net/ghttp/server/session/session_redis.go @@ -24,11 +24,8 @@ func RedisHandlerGet(r *ghttp.Request) { if id == "" { return } - // 当内存中的SESSION存在时不需要读取Redis - if r.Session.Size() > 0 { - return - } - // SESSION不存在时,例如服务重启,自动从Redis读取并恢复数据 + // 应用服务器一般是多个节点构成的集群, + // 当请求中带有SESSION ID时,自动从Redis读取并恢复数据。 value, err := g.Redis().DoVar("GET", id) if err != nil { panic(err)