1
0
mirror of https://github.com/astaxie/beego.git synced 2025-07-30 00:55:30 +00:00

Merge pull request #4137 from phiphi282/change_redis_provider

Add additional options to redis session prov
This commit is contained in:
Ming Deng
2020-08-12 13:38:44 +08:00
committed by GitHub
6 changed files with 216 additions and 81 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,19 +103,21 @@ 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
type Provider struct {
maxlifetime int64
savePath string
poolsize int
password string
dbNum int
poollist *redis.Pool
maxlifetime int64
savePath string
poolsize int
password string
dbNum int
idleTimeout time.Duration
idleCheckFrequency time.Duration
maxRetries int
poollist *redis.Client
}
// SessionInit init redis session
@@ -150,52 +152,44 @@ 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
rp.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,
if len(configs) > 5 {
checkFrequency, err := strconv.Atoi(configs[5])
if err == nil && checkFrequency > 0 {
rp.idleCheckFrequency = time.Duration(checkFrequency) * time.Second
}
}
if len(configs) > 6 {
retries, err := strconv.Atoi(configs[6])
if err == nil && retries > 0 {
rp.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: rp.idleTimeout,
IdleCheckFrequency: rp.idleCheckFrequency,
MaxRetries: rp.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) * time.Second)
}
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
}

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"
@@ -107,12 +107,15 @@ func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
// Provider redis_cluster session provider
type Provider struct {
maxlifetime int64
savePath string
poolsize int
password string
dbNum int
poollist *rediss.ClusterClient
maxlifetime int64
savePath string
poolsize int
password string
dbNum int
idleTimeout time.Duration
idleCheckFrequency time.Duration
maxRetries int
poollist *rediss.ClusterClient
}
// SessionInit init redis_cluster session
@@ -147,11 +150,32 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
} else {
rp.dbNum = 0
}
if len(configs) > 4 {
timeout, err := strconv.Atoi(configs[4])
if err == nil && timeout > 0 {
rp.idleTimeout = time.Duration(timeout) * time.Second
}
}
if len(configs) > 5 {
checkFrequency, err := strconv.Atoi(configs[5])
if err == nil && checkFrequency > 0 {
rp.idleCheckFrequency = time.Duration(checkFrequency) * time.Second
}
}
if len(configs) > 6 {
retries, err := strconv.Atoi(configs[6])
if err == nil && retries > 0 {
rp.maxRetries = retries
}
}
rp.poollist = rediss.NewClusterClient(&rediss.ClusterOptions{
Addrs: strings.Split(rp.savePath, ";"),
Password: rp.password,
PoolSize: rp.poolsize,
Addrs: strings.Split(rp.savePath, ";"),
Password: rp.password,
PoolSize: rp.poolsize,
IdleTimeout: rp.idleTimeout,
IdleCheckFrequency: rp.idleCheckFrequency,
MaxRetries: rp.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"
@@ -107,13 +107,16 @@ func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
// Provider redis_sentinel session provider
type Provider struct {
maxlifetime int64
savePath string
poolsize int
password string
dbNum int
poollist *redis.Client
masterName string
maxlifetime int64
savePath string
poolsize int
password string
dbNum int
idleTimeout time.Duration
idleCheckFrequency time.Duration
maxRetries int
poollist *redis.Client
masterName string
}
// SessionInit init redis_sentinel session
@@ -157,13 +160,34 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
} else {
rp.masterName = "mymaster"
}
if len(configs) > 5 {
timeout, err := strconv.Atoi(configs[4])
if err == nil && timeout > 0 {
rp.idleTimeout = time.Duration(timeout) * time.Second
}
}
if len(configs) > 6 {
checkFrequency, err := strconv.Atoi(configs[5])
if err == nil && checkFrequency > 0 {
rp.idleCheckFrequency = time.Duration(checkFrequency) * time.Second
}
}
if len(configs) > 7 {
retries, err := strconv.Atoi(configs[6])
if err == nil && retries > 0 {
rp.maxRetries = retries
}
}
rp.poollist = redis.NewFailoverClient(&redis.FailoverOptions{
SentinelAddrs: strings.Split(rp.savePath, ";"),
Password: rp.password,
PoolSize: rp.poolsize,
DB: rp.dbNum,
MasterName: rp.masterName,
SentinelAddrs: strings.Split(rp.savePath, ";"),
Password: rp.password,
PoolSize: rp.poolsize,
DB: rp.dbNum,
MasterName: rp.masterName,
IdleTimeout: rp.idleTimeout,
IdleCheckFrequency: rp.idleCheckFrequency,
MaxRetries: rp.maxRetries,
})
return rp.poollist.Ping().Err()