add grand.D for random time.Duration;add checking and removing session files for package gsession

This commit is contained in:
John Guo
2021-03-11 20:05:08 +08:00
parent d72d23c2eb
commit 58362ad143
3 changed files with 98 additions and 26 deletions

View File

@ -7,11 +7,11 @@
package gsession
import (
"fmt"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/grand"
"os"
"time"
@ -36,10 +36,12 @@ type StorageFile struct {
}
var (
DefaultStorageFilePath = gfile.TempDir("gsessions")
DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!")
DefaultStorageFileCryptoEnabled = false
DefaultStorageFileLoopInterval = 10 * time.Second
DefaultStorageFilePath = gfile.TempDir("gsessions")
DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!")
DefaultStorageFileCryptoEnabled = false
DefaultStorageFileLoopInterval = 10 * time.Second
DefaultStorageFileRemoveIntervalMin = time.Hour
DefaultStorageFileRemoveIntervalMax = time.Hour * 24
)
// NewStorageFile creates and returns a file storage object for session.
@ -48,10 +50,10 @@ func NewStorageFile(path ...string) *StorageFile {
if len(path) > 0 && path[0] != "" {
storagePath, _ = gfile.Search(path[0])
if storagePath == "" {
panic(fmt.Sprintf("'%s' does not exist", path[0]))
panic(gerror.Newf("'%s' does not exist", path[0]))
}
if !gfile.IsWritable(storagePath) {
panic(fmt.Sprintf("'%s' is not writable", path[0]))
panic(gerror.Newf("'%s' is not writable", path[0]))
}
}
if storagePath != "" {
@ -65,24 +67,61 @@ func NewStorageFile(path ...string) *StorageFile {
cryptoEnabled: DefaultStorageFileCryptoEnabled,
updatingIdSet: gset.NewStrSet(true),
}
// Batch updates the TTL for session ids timely.
gtimer.AddSingleton(DefaultStorageFileLoopInterval, func() {
//intlog.Print("StorageFile.timer start")
var (
id string
err error
)
for {
if id = s.updatingIdSet.Pop(); id == "" {
break
}
if err = s.doUpdateTTL(id); err != nil {
intlog.Error(err)
gtimer.AddSingleton(DefaultStorageFileLoopInterval, s.updateSessionTimely)
gtimer.AddOnce(
grand.D(DefaultStorageFileRemoveIntervalMin, DefaultStorageFileRemoveIntervalMax),
s.checkAndRemoveSessionTimely,
)
return s
}
// updateSessionTimely batch updates the TTL for sessions timely.
func (s *StorageFile) updateSessionTimely() {
var (
id string
err error
)
// Batch updating sessions.
for {
if id = s.updatingIdSet.Pop(); id == "" {
break
}
if err = s.updateSessionTTl(id); err != nil {
intlog.Error(err)
}
}
}
// checkAndRemoveSessionTimely checks the session storage directory path and removes the expired session files.
func (s *StorageFile) checkAndRemoveSessionTimely() {
defer gtimer.AddOnce(
grand.D(DefaultStorageFileRemoveIntervalMin, DefaultStorageFileRemoveIntervalMax),
s.checkAndRemoveSessionTimely,
)
var (
timestampMilliFile int64
timestampMilliNow = gtime.Now().TimestampMilli()
files, _ = gfile.ScanDirFile(s.path, "*")
timestampMilliBytes = make([]byte, 8)
)
for _, path := range files {
file, err := gfile.OpenWithFlag(path, os.O_RDONLY)
if err != nil {
continue
}
if _, err := file.Read(timestampMilliBytes); err == nil {
timestampMilliFile = gbinary.DecodeToInt64(timestampMilliBytes)
if timestampMilliNow >= timestampMilliFile {
intlog.Printf(
"remove expired session file: %s, result:%v",
path, gfile.Remove(path),
)
}
}
//intlog.Print("StorageFile.timer end")
})
return s
file.Close()
}
}
// SetCryptoKey sets the crypto key for session storage.
@ -229,9 +268,9 @@ func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error {
return nil
}
// doUpdateTTL updates the TTL for session id.
func (s *StorageFile) doUpdateTTL(id string) error {
intlog.Printf("StorageFile.doUpdateTTL: %s", id)
// updateSessionTTL updates the TTL for specified session id.
func (s *StorageFile) updateSessionTTl(id string) error {
intlog.Printf("StorageFile.updateSession: %s", id)
path := s.sessionFilePath(id)
file, err := gfile.OpenWithFlag(path, os.O_WRONLY)
if err != nil {

View File

@ -9,6 +9,7 @@ package grand
import (
"encoding/binary"
"time"
"unsafe"
)
@ -97,6 +98,20 @@ func S(n int, symbols ...bool) string {
return *(*string)(unsafe.Pointer(&b))
}
// D returns a random time.Duration between min and max: [min, max].
func D(min, max time.Duration) time.Duration {
multiple := 1
if min != 0 {
for min%10 == 0 {
multiple *= 10
min /= 10
max /= 10
}
}
n := N(int(min), int(max))
return time.Duration(n * multiple)
}
// Str randomly picks and returns <n> count of chars from given string <s>.
// It also supports unicode string like Chinese/Russian/Japanese, etc.
func Str(s string, n int) string {

View File

@ -11,6 +11,7 @@ package grand_test
import (
"github.com/gogf/gf/text/gstr"
"testing"
"time"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/grand"
@ -73,6 +74,23 @@ func Test_N(t *testing.T) {
})
}
func Test_D(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
t.Assert(grand.D(time.Second, time.Second), time.Second)
}
for i := 0; i < 100; i++ {
t.Assert(grand.D(0, 0), time.Duration(0))
}
for i := 0; i < 100; i++ {
t.AssertIN(
grand.D(1*time.Second, 3*time.Second),
[]time.Duration{1 * time.Second, 2 * time.Second, 3 * time.Second},
)
}
})
}
func Test_Rand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {