add package gkvdb; add kvdb storage feature for ghttp.Server

This commit is contained in:
John
2019-08-22 21:04:30 +08:00
parent 51a156420d
commit 7dffd9d1ff
20 changed files with 795 additions and 113 deletions

View File

@ -0,0 +1,18 @@
package main
import (
"log"
"github.com/dgraph-io/badger"
)
func main() {
// Open the Badger database located in the /tmp/badger directory.
// It will be created if it doesn't exist.
db, err := badger.Open(badger.DefaultOptions("/tmp/badger"))
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Your code here…
}

View File

@ -0,0 +1,17 @@
package main
import (
"fmt"
"github.com/gogf/gf/database/gkvdb"
)
func main() {
key := []byte("key")
//value := []byte("value")
db := gkvdb.Instance()
db.SetPath("/tmp/gkvdb/test")
//db.Set(key, value)
fmt.Println(db.Get(key))
}

View File

@ -8,7 +8,7 @@ import (
func main() {
s := g.Server()
s.SetSessionMaxAge(72000000)
s.SetSessionMaxAge(60)
s.SetSessionIdName("gpadminssid")
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())

View File

@ -172,6 +172,19 @@ func (v *Var) Interfaces() []interface{} {
return gconv.Interfaces(v.Val())
}
// Vars converts and returns <v> as []*Var.
func (v *Var) Vars() []*Var {
array := gconv.Interfaces(v.Val())
if len(array) == 0 {
return nil
}
vars := make([]*Var, len(array))
for k, v := range array {
vars[k] = New(v)
}
return vars
}
// Time converts and returns <v> as time.Time.
// The parameter <format> specifies the format of the time string using gtime,
// eg: Y-m-d H:i:s.

50
database/gkvdb/gkvdb.go Normal file
View File

@ -0,0 +1,50 @@
// Copyright 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.
// Package gkvdb provides a lightweight, embeddable and persistent key-value database.
package gkvdb
import (
"github.com/dgraph-io/badger"
"github.com/gogf/gf/container/gmap"
)
type Options = badger.Options
const (
// Default instance name.
DEFAULT_NAME = "default"
)
var (
// Instance map
instances = gmap.NewStrAnyMap(true)
)
// DefaultOptions returns the default options for gkvdb.
func DefaultOptions(path string) Options {
options := badger.DefaultOptions(path)
options.Logger = nil
options.SyncWrites = false
return options
}
// Instance returns an instance of DB with specified name.
// The <name> param is unnecessary, if <name> is not passed,
// it returns a instance with default name.
func Instance(name ...string) *DB {
instanceName := DEFAULT_NAME
if len(name) > 0 && name[0] != "" {
instanceName = name[0]
}
v := instances.GetOrSetFuncLock(instanceName, func() interface{} {
return New()
})
if v != nil {
return v.(*DB)
}
return nil
}

149
database/gkvdb/gkvdb_db.go Normal file
View File

@ -0,0 +1,149 @@
// Copyright 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.
package gkvdb
import (
"errors"
"time"
"github.com/gogf/gf/os/gfile"
"github.com/dgraph-io/badger"
)
type DB struct {
options Options
badger *badger.DB
}
// New creates and returns a new db object.
func New(options ...Options) *DB {
if len(options) > 0 {
return &DB{options: options[0]}
}
return &DB{options: DefaultOptions("")}
}
// init does lazy initialization for db.
func (db *DB) init() (err error) {
if db.badger != nil {
return nil
}
if !gfile.Exists(db.options.Dir) {
err = gfile.Mkdir(db.options.Dir)
if err != nil {
return
}
}
db.badger, err = badger.Open(db.options)
return
}
// Options return the options of current db object.
func (db *DB) Options() *Options {
return &db.options
}
// SetOptions sets the options for db.
func (db *DB) SetOptions(options Options) error {
if db.badger != nil {
return errors.New("options cannot be changed after db is initialized")
}
db.options = options
return nil
}
// SetPath sets the storage folder path for db.
func (db *DB) SetPath(path string) error {
if db.badger != nil {
return errors.New("options cannot be changed after db is initialized")
}
db.options.Dir = path
db.options.ValueDir = path
return nil
}
// Size returns the data count of current db.
func (db *DB) Size() int64 {
if err := db.init(); err != nil {
return 0
}
lsm, vlog := db.badger.Size()
return lsm + vlog
}
// Set sets <key>-<value> pair data to current db with <ttl>.
// The <ttl> is optional, which is not expired in default.
func (db *DB) Set(key []byte, value []byte, ttl ...time.Duration) (err error) {
if err := db.init(); err != nil {
return err
}
tx := db.Begin(true)
defer tx.Commit()
return tx.Set(key, value, ttl...)
}
// Get returns the value with given key.
// It returns nil if <key> is not found in the db.
func (db *DB) Get(key []byte) (value []byte) {
if err := db.init(); err != nil {
return
}
tx := db.Begin(false)
defer tx.Rollback()
return tx.Get(key)
}
// Delete removed data specified by <key> from current db.
func (db *DB) Delete(key []byte) error {
if err := db.init(); err != nil {
return err
}
tx := db.Begin(true)
defer tx.Commit()
return tx.Delete(key)
}
// Close closes the db.
func (db *DB) Close() error {
if db.badger == nil {
return nil
}
return db.badger.Close()
}
// Iterate is alias of IterateAsc.
// See IterateAsc.
func (db *DB) Iterate(prefix []byte, f func(key, value []byte) bool) {
db.IterateAsc(prefix, f)
}
// IteratorAsc iterates the db in ascending order
// with given callback function <f> starting from <seek>.
// If <seek> is nil it iterates from the beginning of the db.
// If <f> returns true, then it continues iterating; or false to stop.
func (db *DB) IterateAsc(prefix []byte, f func(key, value []byte) bool) {
if err := db.init(); err != nil {
return
}
tx := db.Begin(false)
defer tx.Rollback()
tx.IterateAsc(prefix, f)
}
// IteratorDesc iterates the db in descending order
// with given callback function <f> starting from <seek>.
// If <prefix> is nil it iterates from the beginning of the db.
// If <f> returns true, then it continues iterating; or false to stop.
func (db *DB) IterateDesc(prefix []byte, f func(key, value []byte) bool) {
if err := db.init(); err != nil {
return
}
tx := db.Begin(false)
defer tx.Rollback()
tx.IterateDesc(prefix, f)
}

116
database/gkvdb/gkvdb_tx.go Normal file
View File

@ -0,0 +1,116 @@
// Copyright 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.
package gkvdb
import (
"time"
"github.com/dgraph-io/badger"
)
// TX is the transaction object for db.
type TX struct {
db *DB
txn *badger.Txn
}
// Begin starts the transaction for db.
func (db *DB) Begin(update bool) *TX {
if err := db.init(); err != nil {
return nil
}
return &TX{
db: db,
txn: db.badger.NewTransaction(update),
}
}
// Commit commits the changes and close the transaction.
func (tx *TX) Commit() error {
return tx.txn.Commit()
}
// Rollback discards the changes and close the transaction.
func (tx *TX) Rollback() {
tx.txn.Discard()
}
// Set sets <key>-<value> pair data to current db with <ttl> in this transaction.
// The <ttl> is optional, which is not expired in default.
func (tx *TX) Set(key []byte, value []byte, ttl ...time.Duration) error {
if len(ttl) > 0 && ttl[0] > 0 {
return tx.txn.SetEntry(badger.NewEntry(key, value).WithTTL(ttl[0]))
}
return tx.txn.Set(key, value)
}
// Get returns the value with given key in this transaction.
// It returns nil if <key> is not found in the db.
func (tx *TX) Get(key []byte) (value []byte) {
item, err := tx.txn.Get(key)
if err != nil {
return nil
}
if item.IsDeletedOrExpired() {
return nil
}
value, _ = item.ValueCopy(nil)
return
}
// Delete removed data specified by <key> from current db in this transaction.
func (tx *TX) Delete(key []byte) error {
return tx.txn.Delete(key)
}
// Iterate is alias of IterateAsc.
// See IterateAsc.
func (tx *TX) Iterate(prefix []byte, f func(key, value []byte) bool) {
tx.IterateAsc(prefix, f)
}
// IteratorAsc iterates the db in ascending order
// with given callback function <f> starting from <prefix>.
// If <seek> is nil it iterates from the beginning of the db.
// If <f> returns true, then it continues iterating; or false to stop.
func (tx *TX) IterateAsc(prefix []byte, f func(key, value []byte) bool) {
tx.doIterate(false, prefix, f)
}
// IteratorDesc iterates the db in descending order
// with given callback function <f> starting from <prefix>.
// If <seek> is nil it iterates from the beginning of the db.
// If <f> returns true, then it continues iterating; or false to stop.
func (tx *TX) IterateDesc(prefix []byte, f func(key, value []byte) bool) {
tx.doIterate(true, prefix, f)
}
func (tx *TX) doIterate(reverse bool, prefix []byte, f func(key, value []byte) bool) {
options := badger.DefaultIteratorOptions
if prefix != nil {
options.Prefix = prefix
}
options.Reverse = reverse
options.PrefetchSize = 10
options.PrefetchValues = true
it := tx.txn.NewIterator(options)
defer it.Close()
var k, v []byte
var err error
var item *badger.Item
for it.Rewind(); it.Valid(); it.Next() {
item = it.Item()
k = item.Key()
v, err = item.ValueCopy(nil)
if err != nil {
return
}
if !f(k, v) {
return
}
}
}

View File

@ -0,0 +1,114 @@
// Copyright 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.
package gkvdb_test
import (
"strings"
"testing"
"time"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/database/gkvdb"
"github.com/gogf/gf/test/gtest"
)
func Test_New(t *testing.T) {
gtest.Case(t, func() {
path := "/tmp/gkvdb/" + gconv.String(gtime.Nanosecond())
key := []byte("key")
value := []byte("value")
db := gkvdb.Instance()
db.SetPath(path)
err := db.Set(key, value)
gtest.Assert(err, nil)
gtest.Assert(db.Get(key), value)
gtest.Assert(db.Delete(key), nil)
gtest.Assert(db.Get(key), nil)
})
}
func Test_Set(t *testing.T) {
gtest.Case(t, func() {
path := "/tmp/gkvdb/" + gconv.String(gtime.Nanosecond())
key := []byte("key")
value := []byte("value")
db := gkvdb.Instance()
db.SetPath(path)
err := db.Set(key, value, 100*time.Millisecond)
gtest.Assert(err, nil)
gtest.Assert(db.Get(key), value)
time.Sleep(200 * time.Millisecond)
gtest.Assert(db.Get(key), nil)
})
}
func Test_Iterate(t *testing.T) {
gtest.Case(t, func() {
path := "/tmp/gkvdb/" + gconv.String(gtime.Nanosecond())
db := gkvdb.Instance()
db.SetPath(path)
strArray := garray.NewSortedStringArray()
strArrayReverse := garray.NewSortedStringArrayComparator(func(a, b string) int {
switch strings.Compare(a, b) {
case 0:
return 0
case 1:
return -1
case -1:
return 1
}
return 0
})
for i := 1; i <= 10; i++ {
key := []byte("key_" + gconv.String(i))
strArray.Add(string(key))
strArrayReverse.Add(string(key))
db.Set(key, key)
}
array := garray.New()
db.Iterate(nil, func(key, value []byte) bool {
array.Append(string(key))
return true
})
gtest.Assert(array.Slice(), strArray.Slice())
array = garray.New()
db.IterateDesc(nil, func(key, value []byte) bool {
array.Append(string(key))
return true
})
gtest.Assert(array.Slice(), strArrayReverse.Slice())
array = garray.New()
db.Iterate([]byte("key_1"), func(key, value []byte) bool {
array.Append(key)
return true
})
gtest.Assert(array.Slice(), g.Slice{[]byte("key_1"), []byte("key_10")})
array = garray.New()
db.IterateAsc([]byte("key_1"), func(key, value []byte) bool {
array.Append(key)
return true
})
gtest.Assert(array.Slice(), g.Slice{[]byte("key_1"), []byte("key_10")})
})
}

View File

@ -22,11 +22,6 @@ import (
"github.com/gomodule/redigo/redis"
)
const (
gDEFAULT_POOL_IDLE_TIMEOUT = 60 * time.Second
gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second
)
// Redis client.
type Redis struct {
pool *redis.Pool // Underlying connection pool.
@ -56,9 +51,12 @@ type PoolStats struct {
redis.PoolStats
}
const (
gDEFAULT_POOL_IDLE_TIMEOUT = 60 * time.Second
gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second
)
var (
// Instance map
instances = gmap.NewStrAnyMap(true)
// Pool map.
pools = gmap.NewStrAnyMap(true)
)
@ -106,28 +104,6 @@ func New(config Config) *Redis {
}
}
// Instance returns an instance of redis client with specified group.
// The <group> param is unnecessary, if <group> is not passed,
// it returns a redis instance with default group.
func Instance(name ...string) *Redis {
group := DEFAULT_GROUP_NAME
if len(name) > 0 && name[0] != "" {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
if config, ok := GetConfig(group); ok {
r := New(config)
r.group = group
return r
}
return nil
})
if v != nil {
return v.(*Redis)
}
return nil
}
// Close closes the redis connection pool,
// it will release all connections reserved by this pool.
// It is not necessary to call Close manually.

View File

@ -0,0 +1,36 @@
// Copyright 2017 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.
package gredis
import "github.com/gogf/gf/container/gmap"
var (
// Instance map
instances = gmap.NewStrAnyMap(true)
)
// Instance returns an instance of redis client with specified group.
// The <name> param is unnecessary, if <name> is not passed,
// it returns a redis instance with default configuration group.
func Instance(name ...string) *Redis {
group := DEFAULT_GROUP_NAME
if len(name) > 0 && name[0] != "" {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
if config, ok := GetConfig(group); ok {
r := New(config)
r.group = group
return r
}
return nil
})
if v != nil {
return v.(*Redis)
}
return nil
}

View File

@ -73,6 +73,11 @@ func (j *Json) GetVar(pattern string, def ...interface{}) *gvar.Var {
return gvar.New(j.Get(pattern, def...))
}
// GetVars returns []*gvar.Var with value by given <pattern>.
func (j *Json) GetVars(pattern string, def ...interface{}) []*gvar.Var {
return gvar.New(j.Get(pattern, def...)).Vars()
}
// GetMap gets the value by specified <pattern>,
// and converts it to map[string]interface{}.
func (j *Json) GetMap(pattern string, def ...interface{}) map[string]interface{} {

View File

@ -8,6 +8,7 @@ package g
import (
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/database/gkvdb"
"github.com/gogf/gf/database/gredis"
"github.com/gogf/gf/frame/gins"
"github.com/gogf/gf/net/ghttp"
@ -43,6 +44,12 @@ func Config(name ...string) *gcfg.Config {
return gins.Config(name...)
}
// Cfg is alias of Config.
// See Config.
func Cfg(name ...string) *gcfg.Config {
return Config(name...)
}
// Resource returns an instance of Resource.
// The parameter <name> is the name for the instance.
func Resource(name ...string) *gres.Resource {
@ -66,6 +73,11 @@ func DB(name ...string) gdb.DB {
return gins.Database(name...)
}
// KV returns an instance of gkvdb with specified configuration group name.
func KV(name ...string) *gkvdb.DB {
return gins.KV(name...)
}
// Redis returns an instance of redis client with specified configuration group name.
func Redis(name ...string) *gredis.Redis {
return gins.Redis(name...)

View File

@ -11,6 +11,10 @@ import (
"fmt"
"time"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/database/gkvdb"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/database/gredis"
@ -26,6 +30,7 @@ import (
const (
gFRAME_CORE_COMPONENT_NAME_REDIS = "gf.core.component.redis"
gFRAME_CORE_COMPONENT_NAME_GKVDB = "gf.core.component.gkvdb"
gFRAME_CORE_COMPONENT_NAME_DATABASE = "gf.core.component.database"
)
@ -80,15 +85,16 @@ func Resource(name ...string) *gres.Resource {
return gres.Instance(name...)
}
// 数据库操作对象,使用了连接池
// Database returns an instance of database ORM object
// with specified configuration group name.
func Database(name ...string) gdb.DB {
config := Config()
group := gdb.DEFAULT_GROUP_NAME
if len(name) > 0 && name[0] != "" {
group = name[0]
}
key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_DATABASE, group)
db := instances.GetOrSetFuncLock(key, func() interface{} {
instanceKey := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_DATABASE, group)
db := instances.GetOrSetFuncLock(instanceKey, func() interface{} {
if gdb.GetConfig(group) == nil {
m := config.GetMap("database")
if m == nil {
@ -124,7 +130,7 @@ func Database(name ...string) gdb.DB {
gdb.AddConfigGroup(group, cg)
}
}
addConfigMonitor(key, config)
addConfigMonitor(instanceKey, config)
}
if db, err := gdb.New(name...); err == nil {
return db
@ -139,7 +145,6 @@ func Database(name ...string) gdb.DB {
return nil
}
// 解析数据库配置节点项
func parseDBConfigNode(value interface{}) *gdb.ConfigNode {
nodeMap, ok := value.(map[string]interface{})
if !ok {
@ -217,15 +222,15 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode {
return node
}
// Redis操作对象,使用了连接池
// Redis returns an instance of redis client with specified configuration group name.
func Redis(name ...string) *gredis.Redis {
config := Config()
group := "default"
if len(name) > 0 && name[0] != "" {
group = name[0]
}
key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_REDIS, group)
result := instances.GetOrSetFuncLock(key, func() interface{} {
instanceKey := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_REDIS, group)
result := instances.GetOrSetFuncLock(instanceKey, func() interface{} {
if m := config.GetMap("redis"); m != nil {
// host:port[,db,pass?maxIdle=x&maxActive=x&idleTimeout=x&maxConnLifetime=x]
if v, ok := m[group]; ok {
@ -251,12 +256,12 @@ func Redis(name ...string) *gredis.Redis {
if v, ok := parse["maxConnLifetime"]; ok {
redisConfig.MaxConnLifetime = gconv.Duration(v) * time.Second
}
addConfigMonitor(key, config)
addConfigMonitor(instanceKey, config)
return gredis.New(redisConfig)
}
array, _ = gregex.MatchString(`(.+):(\d+),{0,1}(\d*),{0,1}(.*)`, line)
if len(array) == 5 {
addConfigMonitor(key, config)
addConfigMonitor(instanceKey, config)
return gredis.New(gredis.Config{
Host: array[1],
Port: gconv.Int(array[2]),
@ -280,17 +285,42 @@ func Redis(name ...string) *gredis.Redis {
return nil
}
// 添加对单例对象的配置文件inotify监控
// KV returns an instance of gkvdb with specified configuration group name.
func KV(name ...string) *gkvdb.DB {
config := Config()
group := "default"
if len(name) > 0 && name[0] != "" {
group = name[0]
}
instanceKey := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_GKVDB, group)
result := instances.GetOrSetFuncLock(instanceKey, func() interface{} {
key := fmt.Sprintf("kvdb.%s", group)
if s := config.GetString(key); s != "" {
db := gkvdb.Instance(group)
parse, _ := gstr.Parse(s)
if value, ok := parse["path"]; ok {
db.SetPath(gconv.String(value))
}
if value, ok := parse["sync"]; ok {
db.Options().SyncWrites = gconv.Bool(value)
}
addConfigMonitor(instanceKey, config)
return db
} else {
glog.Errorf(`incomplete configuration for gkvdb: "%s" node not found in config file "%s"`, key, config.FilePath())
}
return nil
})
if result != nil {
return result.(*gkvdb.DB)
}
return nil
}
func addConfigMonitor(key string, config *gcfg.Config) {
// 使用gfsnotify进行文件监控当配置文件有任何变化时清空对象单例缓存
if path := config.FilePath(); path != "" {
if path := config.FilePath(); path != "" && gfile.Exists(path) {
gfsnotify.Add(path, func(event *gfsnotify.Event) {
instances.Remove(key)
})
}
}
// 模板内置方法config
func funcConfig(pattern string, file ...interface{}) string {
return Config().GetString(pattern, file...)
}

View File

@ -0,0 +1,44 @@
// Copyright 2017 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.
package gins_test
import (
"testing"
"time"
"github.com/gogf/gf/frame/gins"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/test/gtest"
)
func Test_KV(t *testing.T) {
config := `
[kvdb]
default = "path=/tmp/gkvdb&sync=false"
cache = "path=/tmp/gkvdb-cache&sync=true"
`
path := "config.toml"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
defer gins.Config().Clear()
// for gfsnotify callbacks to refresh cache of config file
time.Sleep(500 * time.Millisecond)
gtest.Case(t, func() {
kvDefault := gins.KV()
kvCache := gins.KV("cache")
key := []byte("key")
value := []byte("value")
err := kvDefault.Set(key, value)
gtest.Assert(err, nil)
gtest.Assert(kvDefault.Get(key), value)
gtest.Assert(kvCache.Get(key), nil)
})
}

1
go.mod
View File

@ -3,6 +3,7 @@ module github.com/gogf/gf
require (
github.com/BurntSushi/toml v0.3.1
github.com/clbanning/mxj v1.8.4
github.com/dgraph-io/badger v1.6.0
github.com/fatih/structs v1.1.0
github.com/fsnotify/fsnotify v1.4.7
github.com/gf-third/mysql v1.4.2

49
go.sum
View File

@ -1,14 +1,31 @@
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/badger v1.6.0 h1:DshxFxZWXUcO0xX476VJC07Xsr6ZCBVRHKZ93Oh7Evo=
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gf-third/mysql v1.4.2 h1:f1M5CNFUG3WkE07UOomtu4o0n/KJKeuUUf5Nc9ZFXs4=
github.com/gf-third/mysql v1.4.2/go.mod h1:+dd90V663ppI2fV5uQ6+rHk0u8KCyU6FkG8Um8Cx3ms=
github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
@ -16,23 +33,52 @@ github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278 h1:DZo48DQFIDo/YWjUeFip1dfJztBhRuaxfUnPd+gAfcs=
github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278/go.mod h1:Xk7G0nwBiIloTMbLddk4WWJOqi4i/JLhadLd0HUXO30=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/theckman/go-flock v0.7.1 h1:YdJyIjDuQdEU7voZ9YaeXSO4OnrxdI+WejPUwyZ/Txs=
github.com/theckman/go-flock v0.7.1/go.mod h1:kjuth3y9VJ2aNlkNEO99G/8lp9fMIKaGyBmh84IBheM=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -40,8 +86,11 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -17,6 +17,8 @@ import (
"strings"
"time"
"github.com/gogf/gf/database/gkvdb"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/container/gtype"
@ -45,7 +47,8 @@ type (
serveCache *gcache.Cache // 服务注册路由内存缓存
routesMap map[string][]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断)
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
sessions *gcache.Cache // Session内存
sessions *gcache.Cache // Session内存存
sessionStorage *gkvdb.DB // Session物理存储
logger *glog.Logger // 日志管理对象
}
@ -209,6 +212,7 @@ func GetServer(name ...interface{}) *Server {
serveCache: gcache.New(),
routesMap: make(map[string][]registeredRouteItem),
sessions: gcache.New(),
sessionStorage: gkvdb.New(gkvdb.DefaultOptions(defaultServerConfig.SessionStoragePath)),
servedCount: gtype.NewInt(),
logger: glog.New(),
}

View File

@ -38,73 +38,75 @@ type LogHandler func(r *Request, error ...interface{})
// HTTP Server 设置结构体,静态配置
type ServerConfig struct {
Addr string // 监听IP和端口监听本地所有IP使用":端口"(支持多个地址,使用","号分隔)
HTTPSAddr string // HTTPS服务监听地址(支持多个地址,使用","号分隔)
HTTPSCertPath string // HTTPS证书文件路径
HTTPSKeyPath string // HTTPS签名文件路径
Handler http.Handler // 默认的处理函数
ReadTimeout time.Duration // 读取超时
WriteTimeout time.Duration // 写入超时
IdleTimeout time.Duration // 等待超时
MaxHeaderBytes int // 最大的header长度
TLSConfig tls.Config // HTTPS证书配置
KeepAlive bool // 是否开启长连接
ServerAgent string // Server Agent
View *gview.View // 模板引擎对象
Rewrites map[string]string // URI Rewrite重写配置
IndexFiles []string // Static: 默认访问的文件列表
IndexFolder bool // Static: 如果访问目录是否显示目录列表
ServerRoot string // Static: 服务器服务的本地目录根路径(检索优先级比StaticPaths低)
SearchPaths []string // Static: 静态文件搜索目录(包含ServerRoot按照优先级进行排序)
StaticPaths []staticPathItem // Static: 静态文件目录映射(按照优先级进行排序)
FileServerEnabled bool // Static: 是否允许静态文件服务(通过静态文件服务方法调用自动识别)
CookieMaxAge int64 // Cookie: 有效期(秒)
CookiePath string // Cookie: 有效Path(注意同时也会影响SessionID)
CookieDomain string // Cookie: 有效Domain(注意同时也会影响SessionID)
SessionMaxAge int64 // Session: 有效期(秒)
SessionIdName string // Session: SessionId
DenyIps []string // Security: 不允许访问的ip列表支持ip前缀过滤如: 10 将不允许10开头的ip访问
AllowIps []string // Security: 允许访问的ip列表支持ip前缀过滤如: 10 将允许10开头的ip访问
DenyRoutes []string // Security: 允许访问的路由规则列表
LogPath string // Logging: 存放日志的目录路径(默认为空,表示不写文件)
LogHandler LogHandler // Logging: 日志配置: 自定义日志处理回调方法(默认为空)
LogStdout bool // Logging: 是否打印日志到终端(默认开启)
ErrorLogEnabled bool // Logging: 是否开启error log(默认开启)
AccessLogEnabled bool // Logging: 是否开启access log(默认关闭)
NameToUriType int // Mess: 服务注册时对象和方法名称转换为URI时的规则
GzipContentTypes []string // Mess: 允许进行gzip压缩的文件类型
DumpRouteMap bool // Mess: 是否在程序启动时默认打印路由表信息
RouterCacheExpire int // Mess: 路由检索缓存过期时间(秒)
Addr string // 监听IP和端口监听本地所有IP使用":端口"(支持多个地址,使用","号分隔)
HTTPSAddr string // HTTPS服务监听地址(支持多个地址,使用","号分隔)
HTTPSCertPath string // HTTPS证书文件路径
HTTPSKeyPath string // HTTPS签名文件路径
Handler http.Handler // 默认的处理函数
ReadTimeout time.Duration // 读取超时
WriteTimeout time.Duration // 写入超时
IdleTimeout time.Duration // 等待超时
MaxHeaderBytes int // 最大的header长度
TLSConfig tls.Config // HTTPS证书配置
KeepAlive bool // 是否开启长连接
ServerAgent string // Server Agent
View *gview.View // 模板引擎对象
Rewrites map[string]string // URI Rewrite重写配置
IndexFiles []string // Static: 默认访问的文件列表
IndexFolder bool // Static: 如果访问目录是否显示目录列表
ServerRoot string // Static: 服务器服务的本地目录根路径(检索优先级比StaticPaths低)
SearchPaths []string // Static: 静态文件搜索目录(包含ServerRoot按照优先级进行排序)
StaticPaths []staticPathItem // Static: 静态文件目录映射(按照优先级进行排序)
FileServerEnabled bool // Static: 是否允许静态文件服务(通过静态文件服务方法调用自动识别)
CookieMaxAge int64 // Cookie: 有效期(秒)
CookiePath string // Cookie: 有效Path(注意同时也会影响SessionID)
CookieDomain string // Cookie: 有效Domain(注意同时也会影响SessionID)
SessionMaxAge int64 // Session: 有效期(秒)
SessionIdName string // Session: SessionId
SessionStoragePath string // Session: 存储路径
DenyIps []string // Security: 允许访问的ip列表支持ip前缀过滤如: 10 将允许10开头的ip访问
AllowIps []string // Security: 允许访问的ip列表支持ip前缀过滤如: 10 将仅允许10开头的ip访问
DenyRoutes []string // Security: 不允许访问的路由规则列表
LogPath string // Logging: 存放日志的目录路径(默认为空,表示不写文件)
LogHandler LogHandler // Logging: 日志配置: 自定义日志处理回调方法(默认为空)
LogStdout bool // Logging: 是否打印日志到终端(默认开启)
ErrorLogEnabled bool // Logging: 是否开启error log(默认开启)
AccessLogEnabled bool // Logging: 是否开启access log(默认关闭)
NameToUriType int // Mess: 服务注册时对象和方法名称转换为URI时的规则
GzipContentTypes []string // Mess: 允许进行gzip压缩的文件类型
DumpRouteMap bool // Mess: 是否在程序启动时默认打印路由表信息
RouterCacheExpire int // Mess: 路由检索缓存过期时间(秒)
}
// 默认HTTP Server配置
var defaultServerConfig = ServerConfig{
Addr: "",
HTTPSAddr: "",
Handler: nil,
ReadTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 1024,
KeepAlive: true,
View: gview.Instance(),
IndexFiles: []string{"index.html", "index.htm"},
IndexFolder: false,
ServerAgent: "gf http server",
ServerRoot: "",
StaticPaths: make([]staticPathItem, 0),
FileServerEnabled: false,
CookieMaxAge: gDEFAULT_COOKIE_MAX_AGE,
CookiePath: gDEFAULT_COOKIE_PATH,
CookieDomain: "",
SessionMaxAge: gDEFAULT_SESSION_MAX_AGE,
SessionIdName: gDEFAULT_SESSION_ID_NAME,
LogStdout: true,
ErrorLogEnabled: true,
AccessLogEnabled: false,
DumpRouteMap: true,
RouterCacheExpire: 60,
Rewrites: make(map[string]string),
Addr: "",
HTTPSAddr: "",
Handler: nil,
ReadTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 1024,
KeepAlive: true,
View: gview.Instance(),
IndexFiles: []string{"index.html", "index.htm"},
IndexFolder: false,
ServerAgent: "gf http server",
ServerRoot: "",
StaticPaths: make([]staticPathItem, 0),
FileServerEnabled: false,
CookieMaxAge: gDEFAULT_COOKIE_MAX_AGE,
CookiePath: gDEFAULT_COOKIE_PATH,
CookieDomain: "",
SessionMaxAge: gDEFAULT_SESSION_MAX_AGE,
SessionIdName: gDEFAULT_SESSION_ID_NAME,
SessionStoragePath: gfile.TempDir() + gfile.Separator + "gfsessions",
LogStdout: true,
ErrorLogEnabled: true,
AccessLogEnabled: false,
DumpRouteMap: true,
RouterCacheExpire: 60,
Rewrites: make(map[string]string),
}
// 获取默认的http server设置

View File

@ -6,7 +6,12 @@
package ghttp
import "github.com/gogf/gf/os/glog"
import (
"fmt"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/os/glog"
)
// 设置http server参数 - SessionMaxAge
func (s *Server) SetSessionMaxAge(age int64) {
@ -26,6 +31,22 @@ func (s *Server) SetSessionIdName(name string) {
s.config.SessionIdName = name
}
// 设置http server参数 - SessionStoragePath
func (s *Server) SetSessionStoragePath(path string) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
realPath, _ := gfile.Search(path)
if realPath != "" {
glog.Fatal(fmt.Sprintf(`[ghttp] SetSessionStoragePath failed: '%s' does not exist`, path))
}
s.config.SessionStoragePath = realPath
if err := s.sessionStorage.SetPath(realPath); err != nil {
glog.Fatal(fmt.Sprintf(`[ghttp] SetSessionStoragePath failed: %s`, err.Error()))
}
}
// 获取http server参数 - SessionMaxAge
func (s *Server) GetSessionMaxAge() int64 {
return s.config.SessionMaxAge
@ -35,3 +56,8 @@ func (s *Server) GetSessionMaxAge() int64 {
func (s *Server) GetSessionIdName() string {
return s.config.SessionIdName
}
// 获取http server参数 - SessionStoragePath
func (s *Server) GetSessionStoragePath() string {
return s.config.SessionStoragePath
}

View File

@ -57,9 +57,19 @@ func (s *Session) init() {
s.server = s.request.Server
if id := s.request.GetSessionId(); id != "" {
if v := s.server.sessions.Get(id); v != nil {
// 纯内存查询
s.id = id
s.data = v.(*gmap.StrAnyMap)
return
} else {
// 持久化恢复
data := s.server.sessionStorage.Get([]byte(id))
if data != nil {
s.id = id
s.data = gmap.NewStrAnyMap(true)
s.Restore(data)
return
}
}
}
// 否则执行初始化创建
@ -165,6 +175,16 @@ func (s *Session) Clear() {
// 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除)
func (s *Session) UpdateExpire() {
if len(s.id) > 0 && s.data.Size() > 0 {
// 优先持久化存储
if s.dirty {
data, _ := s.Export()
s.server.sessionStorage.Set(
[]byte(s.id),
data,
time.Duration(s.server.GetSessionMaxAge())*time.Second,
)
}
// 其次更新内存TTL
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000)
}
}