1
0
mirror of https://github.com/astaxie/beego.git synced 2025-06-12 19:40:40 +00:00

Add additional options to redis session prov

Adding option for frequency of checking timed out connections as well as
an option to specify retries.

These changes make redis provider more stable since connection problems
are becoming fewer.

Since redigo does not have this options and since redis_sentinel and
redis_cluster are using go-redis as a client, this commit changes from
redigo to go-redis for redis session provider.

Added tests for redis session provider as well.
This commit is contained in:
Phillip Stagnet
2020-08-06 11:14:36 +02:00
parent 2fce8f9d1b
commit ec55edfbc4
6 changed files with 189 additions and 94 deletions

View File

@ -41,7 +41,7 @@ import (
"github.com/astaxie/beego/pkg/session"
"github.com/gomodule/redigo/redis"
"github.com/go-redis/redis/v7"
)
var redispder = &Provider{}
@ -51,7 +51,7 @@ var MaxPoolSize = 100
// SessionStore redis session store
type SessionStore struct {
p *redis.Pool
p *redis.Client
sid string
lock sync.RWMutex
values map[interface{}]interface{}
@ -103,9 +103,8 @@ func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
if err != nil {
return
}
c := rs.p.Get()
defer c.Close()
c.Do("SETEX", rs.sid, rs.maxlifetime, string(b))
c := rs.p
c.Set(rs.sid, string(b), time.Duration(rs.maxlifetime)*time.Second)
}
// Provider redis session provider
@ -115,7 +114,7 @@ type Provider struct {
poolsize int
password string
dbNum int
poollist *redis.Pool
poollist *redis.Client
}
// SessionInit init redis session
@ -157,45 +156,40 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
idleTimeout = time.Duration(timeout) * time.Second
}
}
rp.poollist = &redis.Pool{
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", rp.savePath)
if err != nil {
return nil, err
}
if rp.password != "" {
if _, err = c.Do("AUTH", rp.password); err != nil {
c.Close()
return nil, err
}
}
// some redis proxy such as twemproxy is not support select command
if rp.dbNum > 0 {
_, err = c.Do("SELECT", rp.dbNum)
if err != nil {
c.Close()
return nil, err
}
}
return c, err
},
MaxIdle: rp.poolsize,
var idleCheckFrequency time.Duration = 0
if len(configs) > 5 {
checkFrequency, err := strconv.Atoi(configs[5])
if err == nil && checkFrequency > 0 {
idleCheckFrequency = time.Duration(checkFrequency) * time.Second
}
}
var maxRetries = 0
if len(configs) > 6 {
retries, err := strconv.Atoi(configs[6])
if err == nil && retries > 0 {
maxRetries = retries
}
}
rp.poollist.IdleTimeout = idleTimeout
rp.poollist = redis.NewClient(&redis.Options{
Addr: rp.savePath,
Password: rp.password,
PoolSize: rp.poolsize,
DB: rp.dbNum,
IdleTimeout: idleTimeout,
IdleCheckFrequency: idleCheckFrequency,
MaxRetries: maxRetries,
})
return rp.poollist.Get().Err()
return rp.poollist.Ping().Err()
}
// SessionRead read redis session by sid
func (rp *Provider) SessionRead(sid string) (session.Store, error) {
c := rp.poollist.Get()
defer c.Close()
var kv map[interface{}]interface{}
kvs, err := redis.String(c.Do("GET", sid))
if err != nil && err != redis.ErrNil {
kvs, err := rp.poollist.Get(sid).Result()
if err != nil && err != redis.Nil {
return nil, err
}
if len(kvs) == 0 {
@ -212,10 +206,9 @@ func (rp *Provider) SessionRead(sid string) (session.Store, error) {
// SessionExist check redis session exist by sid
func (rp *Provider) SessionExist(sid string) (bool, error) {
c := rp.poollist.Get()
defer c.Close()
c := rp.poollist
if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 {
if existed, err := c.Exists(sid).Result(); err != nil || existed == 0 {
return false, err
}
return true, nil
@ -223,27 +216,24 @@ func (rp *Provider) SessionExist(sid string) (bool, error) {
// SessionRegenerate generate new sid for redis session
func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
c := rp.poollist.Get()
defer c.Close()
if existed, _ := redis.Int(c.Do("EXISTS", oldsid)); existed == 0 {
c := rp.poollist
if existed, _ := c.Exists(oldsid).Result(); existed == 0 {
// oldsid doesn't exists, set the new sid directly
// ignore error here, since if it return error
// the existed value will be 0
c.Do("SET", sid, "", "EX", rp.maxlifetime)
c.Do(c.Context(), "SET", sid, "", "EX", rp.maxlifetime)
} else {
c.Do("RENAME", oldsid, sid)
c.Do("EXPIRE", sid, rp.maxlifetime)
c.Rename(oldsid, sid)
c.Expire(sid, time.Duration(rp.maxlifetime))
}
return rp.SessionRead(sid)
}
// SessionDestroy delete redis session by id
func (rp *Provider) SessionDestroy(sid string) error {
c := rp.poollist.Get()
defer c.Close()
c := rp.poollist
c.Do("DEL", sid)
c.Del(sid)
return nil
}
@ -259,3 +249,4 @@ func (rp *Provider) SessionAll() int {
func init() {
session.Register("redis", redispder)
}

View File

@ -0,0 +1,88 @@
package redis
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/astaxie/beego/pkg/session"
)
func TestRedis(t *testing.T) {
sessionConfig := &session.ManagerConfig{
CookieName: "gosessionid",
EnableSetCookie: true,
Gclifetime: 3600,
Maxlifetime: 3600,
Secure: false,
CookieLifeTime: 3600,
ProviderConfig: "127.0.0.1:6379,100,,0,30",
}
globalSession, err := session.NewManager("redis", sessionConfig)
if err != nil {
t.Fatal("could not create manager:", err)
}
go globalSession.GC()
r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
sess, err := globalSession.SessionStart(w, r)
if err != nil {
t.Fatal("session start failed:", err)
}
defer sess.SessionRelease(w)
// SET AND GET
err = sess.Set("username", "astaxie")
if err != nil {
t.Fatal("set username failed:", err)
}
username := sess.Get("username")
if username != "astaxie" {
t.Fatal("get username failed")
}
// DELETE
err = sess.Delete("username")
if err != nil {
t.Fatal("delete username failed:", err)
}
username = sess.Get("username")
if username != nil {
t.Fatal("delete username failed")
}
// FLUSH
err = sess.Set("username", "astaxie")
if err != nil {
t.Fatal("set failed:", err)
}
err = sess.Set("password", "1qaz2wsx")
if err != nil {
t.Fatal("set failed:", err)
}
username = sess.Get("username")
if username != "astaxie" {
t.Fatal("get username failed")
}
password := sess.Get("password")
if password != "1qaz2wsx" {
t.Fatal("get password failed")
}
err = sess.Flush()
if err != nil {
t.Fatal("flush failed:", err)
}
username = sess.Get("username")
if username != nil {
t.Fatal("flush failed")
}
password = sess.Get("password")
if password != nil {
t.Fatal("flush failed")
}
sess.SessionRelease(w)
}

View File

@ -34,7 +34,7 @@ package redis_cluster
import (
"github.com/astaxie/beego/pkg/session"
rediss "github.com/go-redis/redis"
rediss "github.com/go-redis/redis/v7"
"net/http"
"strconv"
"strings"
@ -147,11 +147,35 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
} else {
rp.dbNum = 0
}
var idleTimeout time.Duration = 0
if len(configs) > 4 {
timeout, err := strconv.Atoi(configs[4])
if err == nil && timeout > 0 {
idleTimeout = time.Duration(timeout) * time.Second
}
}
var idleCheckFrequency time.Duration = 0
if len(configs) > 5 {
checkFrequency, err := strconv.Atoi(configs[5])
if err == nil && checkFrequency > 0 {
idleCheckFrequency = time.Duration(checkFrequency) * time.Second
}
}
var maxRetries = 0
if len(configs) > 6 {
retries, err := strconv.Atoi(configs[6])
if err == nil && retries > 0 {
maxRetries = retries
}
}
rp.poollist = rediss.NewClusterClient(&rediss.ClusterOptions{
Addrs: strings.Split(rp.savePath, ";"),
Password: rp.password,
PoolSize: rp.poolsize,
IdleTimeout: idleTimeout,
IdleCheckFrequency: idleCheckFrequency,
MaxRetries: maxRetries,
})
return rp.poollist.Ping().Err()
}

View File

@ -34,7 +34,7 @@ package redis_sentinel
import (
"github.com/astaxie/beego/pkg/session"
"github.com/go-redis/redis"
"github.com/go-redis/redis/v7"
"net/http"
"strconv"
"strings"
@ -157,6 +157,27 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
} else {
rp.masterName = "mymaster"
}
var idleTimeout time.Duration = 0
if len(configs) > 5 {
timeout, err := strconv.Atoi(configs[4])
if err == nil && timeout > 0 {
idleTimeout = time.Duration(timeout) * time.Second
}
}
var idleCheckFrequency time.Duration = 0
if len(configs) > 6 {
checkFrequency, err := strconv.Atoi(configs[5])
if err == nil && checkFrequency > 0 {
idleCheckFrequency = time.Duration(checkFrequency) * time.Second
}
}
var maxRetries = 0
if len(configs) > 7 {
retries, err := strconv.Atoi(configs[6])
if err == nil && retries > 0 {
maxRetries = retries
}
}
rp.poollist = redis.NewFailoverClient(&redis.FailoverOptions{
SentinelAddrs: strings.Split(rp.savePath, ";"),
@ -164,6 +185,9 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
PoolSize: rp.poolsize,
DB: rp.dbNum,
MasterName: rp.masterName,
IdleTimeout: idleTimeout,
IdleCheckFrequency: idleCheckFrequency,
MaxRetries: maxRetries,
})
return rp.poollist.Ping().Err()