improve ghttp for session storage with redis server

This commit is contained in:
John
2019-07-28 13:10:34 +08:00
parent bd4d273e1c
commit a36e00efeb
4 changed files with 133 additions and 147 deletions

View File

@ -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...))
}

View File

@ -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()
}
})
}

View File

@ -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)
}
}
}

View File

@ -1,4 +1,4 @@
package gf
const VERSION = "v1.8.2"
const VERSION = "v1.8.3"
const AUTHORS = "john<john@goframe.org>"