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:
@ -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)
|
||||
}
|
||||
|
||||
|
88
pkg/session/redis/sess_redis_test.go
Normal file
88
pkg/session/redis/sess_redis_test.go
Normal 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)
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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()
|
||||
|
Reference in New Issue
Block a user