Skip to content

Commit

Permalink
use go-redis
Browse files Browse the repository at this point in the history
  • Loading branch information
Skyenought committed Nov 24, 2023
1 parent b26485e commit afa1607
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 169 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ require (
github.com/gorilla/securecookie v1.1.2
github.com/gorilla/sessions v1.2.2
github.com/mna/redisc v1.4.0
github.com/redis/go-redis/v9 v9.3.0 // indirect
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6HaZIxD39I=
github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM=
github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7 h1:PtwsQyQJGxf8iaPptPNaduEIu9BnrNms+pcRdHAxZaM=
Expand All @@ -7,6 +9,8 @@ github.com/bytedance/mockey v1.2.1/go.mod h1:+Jm/fzWZAuhEDrPXVjDf/jLM2BlLXJkwk94
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.8.1 h1:NqAHCaGaTzro0xMmnTCLUyRlbEP6r8MCA1cJUrH3Pu4=
github.com/bytedance/sonic v1.8.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
Expand All @@ -17,6 +21,8 @@ github.com/cloudwego/netpoll v0.5.0/go.mod h1:xVefXptcyheopwNDZjDPcfU6kIjZXZ4nY5
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -52,6 +58,8 @@ github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuw
github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
Expand Down
69 changes: 4 additions & 65 deletions redis/redistore.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,8 @@
package redis

import (
"bytes"
"encoding/base32"
"encoding/gob"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
Expand All @@ -62,69 +58,12 @@ import (
"github.com/gomodule/redigo/redis"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
hs "github.com/hertz-contrib/sessions"
)

// Amount of time for cookies/redis keys to expire.
var sessionExpire = 86400 * 30

// SessionSerializer provides an interface hook for alternative serializers
type SessionSerializer interface {
Deserialize(d []byte, ss *sessions.Session) error
Serialize(ss *sessions.Session) ([]byte, error)
}

// JSONSerializer encode the session map to JSON.
type JSONSerializer struct{}

// Serialize to JSON. Will err if there are unmarshalable key values
func (s JSONSerializer) Serialize(ss *sessions.Session) ([]byte, error) {
m := make(map[string]interface{}, len(ss.Values))
for k, v := range ss.Values {
ks, ok := k.(string)
if !ok {
err := fmt.Errorf("non-string key value, cannot serialize session to JSON: %v", k)
hlog.Errorf("redistore.JSONSerializer.serialize() Error: %v", err)
return nil, err
}
m[ks] = v
}
return json.Marshal(m)
}

// Deserialize back to map[string]interface{}
func (s JSONSerializer) Deserialize(d []byte, ss *sessions.Session) error {
m := make(map[string]interface{})
err := json.Unmarshal(d, &m)
if err != nil {
hlog.Errorf("redistore.JSONSerializer.deserialize() Error: %v", err)
return err
}
for k, v := range m {
ss.Values[k] = v
}
return nil
}

// GobSerializer uses gob package to encode the session map
type GobSerializer struct{}

// Serialize using gob
func (s GobSerializer) Serialize(ss *sessions.Session) ([]byte, error) {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
err := enc.Encode(ss.Values)
if err == nil {
return buf.Bytes(), nil
}
return nil, err
}

// Deserialize back to map[interface{}]interface{}
func (s GobSerializer) Deserialize(d []byte, ss *sessions.Session) error {
dec := gob.NewDecoder(bytes.NewBuffer(d))
return dec.Decode(&ss.Values)
}

// RediStore stores sessions in a redis backend.
type RediStore struct {
Pool *redis.Pool
Expand All @@ -133,7 +72,7 @@ type RediStore struct {
DefaultMaxAge int // default Redis TTL for a MaxAge == 0 session
maxLength int
keyPrefix string
serializer SessionSerializer
serializer hs.Serializer
}

// SetMaxLength sets RediStore.maxLength if the `l` argument is greater or equal 0
Expand All @@ -154,7 +93,7 @@ func (s *RediStore) SetKeyPrefix(p string) {
}

// SetSerializer sets the serializer
func (s *RediStore) SetSerializer(ss SessionSerializer) {
func (s *RediStore) SetSerializer(ss hs.Serializer) {
s.serializer = ss
}

Expand Down Expand Up @@ -253,7 +192,7 @@ func NewRediStoreWithPool(pool *redis.Pool, keyPairs ...[]byte) (*RediStore, err
DefaultMaxAge: 60 * 20, // 20 minutes seems like a reasonable default
maxLength: 4096,
keyPrefix: "session_",
serializer: GobSerializer{},
serializer: hs.GobSerializer{},
}
_, err := rs.ping()
return rs, err
Expand Down
3 changes: 2 additions & 1 deletion redis/redistore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
"bytes"
"encoding/base64"
"encoding/gob"
hs "github.com/hertz-contrib/sessions"
"net/http"
"net/http/httptest"
"testing"
Expand Down Expand Up @@ -415,7 +416,7 @@ func TestRediStore(t *testing.T) {
{
addr := setup()
store, err := NewRediStore(10, "tcp", addr, "", []byte("secret-key"))
store.SetSerializer(JSONSerializer{})
store.SetSerializer(hs.JSONSerializer{})
if err != nil {
t.Fatal(err.Error())
}
Expand Down
134 changes: 38 additions & 96 deletions rediscluster/redisc_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,59 +47,58 @@
package rediscluster

import (
"context"
"encoding/base32"
"errors"
gredis "github.com/redis/go-redis/v9"
"net/http"
"strings"
"time"

hs "github.com/hertz-contrib/sessions"

"github.com/gomodule/redigo/redis"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
rredis "github.com/hertz-contrib/sessions/redis"
"github.com/mna/redisc"
hs "github.com/hertz-contrib/sessions"
)

var sessionExpire = 86400 * 30

type Store struct {
Cluster *redisc.Cluster
Rdb *gredis.ClusterClient
Codecs []securecookie.Codec
Opts *sessions.Options // default configuration
DefaultMaxAge int // default Redis TTL for a MaxAge == 0 session
maxLength int
keyPrefix string
serializer rredis.SessionSerializer
serializer hs.Serializer
}

func (s *Store) Options(options hs.Options) {
s.Opts = options.ToGorillaOptions()
}

// NewStoreWithCluster returns a new rediscluster.Store by setting redisc.Cluster
func NewStoreWithCluster(cluster *redisc.Cluster, kvs ...[]byte) (*Store, error) {
// NewStoreWithOption returns a new rediscluster.Store by setting redisc.Cluster
func NewStoreWithOption(opt *gredis.ClusterOptions, kvs ...[]byte) (*Store, error) {
rs := &Store{
Cluster: cluster,
Codecs: securecookie.CodecsFromPairs(kvs...),
Rdb: gredis.NewClusterClient(opt),
Codecs: securecookie.CodecsFromPairs(kvs...),
Opts: &sessions.Options{
Path: "/",
MaxAge: sessionExpire,
},
DefaultMaxAge: 60 * 20, // 20 minutes seems like a reasonable default
maxLength: 4096,
keyPrefix: "session_",
serializer: rredis.GobSerializer{},
serializer: hs.GobSerializer{},
}

err := cluster.Refresh()
err := rs.Rdb.ForEachShard(context.Background(), func(ctx context.Context, shard *gredis.Client) error {
return shard.Ping(ctx).Err()
})
return rs, err
}

// NewStore returns a new rediscluster.Store
func NewStore(maxIdle int, network string, startupNodes []string, password string, kvs ...[]byte) (*Store, error) {
return NewStoreWithCluster(newCluster(startupNodes, CreateDefaultPool(maxIdle, network, password)), kvs...)
func NewStore(maxIdle int, addrs []string, password string, newClient func(opt *gredis.Options) *gredis.Client, kvs ...[]byte) (*Store, error) {
return NewStoreWithOption(newOption(addrs, password, maxIdle, newClient), kvs...)
}

func (s *Store) Get(r *http.Request, name string) (*sessions.Session, error) {
Expand Down Expand Up @@ -151,7 +150,7 @@ func (s *Store) Save(r *http.Request, w http.ResponseWriter, session *sessions.S
}

func (s *Store) Close() error {
return s.Cluster.Close()
return s.Rdb.Close()
}

// SetMaxLength sets RedisClusterStore.maxLength if the `l` argument is greater or equal 0
Expand All @@ -172,24 +171,16 @@ func (s *Store) SetKeyPrefix(p string) {
}

// SetSerializer sets the serializer
func (s *Store) SetSerializer(ss rredis.SessionSerializer) {
func (s *Store) SetSerializer(ss hs.Serializer) {
s.serializer = ss
}

func (s *Store) load(session *sessions.Session) (bool, error) {
conn := s.Cluster.Get()
defer conn.Close()
if err := conn.Err(); err != nil {
return false, err
}
data, err := conn.Do("GET", s.keyPrefix+session.ID)
if err != nil {
return false, err
}
if data == nil {
return false, nil // no data was associated with this key
res := s.Rdb.Get(context.Background(), s.keyPrefix+session.ID)
if res == nil {
return false, nil
}
b, err := redis.Bytes(data, err)
b, err := res.Bytes()
if err != nil {
return false, err
}
Expand All @@ -205,36 +196,25 @@ func (s *Store) save(session *sessions.Session) error {
if s.maxLength != 0 && len(b) > s.maxLength {
return errors.New("SessionStore: the value to store is too big")
}
conn := s.Cluster.Get()
defer conn.Close()
if err = conn.Err(); err != nil {
return err
}
age := session.Options.MaxAge
if age == 0 {
age = s.DefaultMaxAge
}
_, err = conn.Do("SETEX", s.keyPrefix+session.ID, age, b)
err = s.Rdb.SetEx(context.Background(), s.keyPrefix+session.ID, b, time.Duration(age)*time.Second).Err()
return err
}

func (s *Store) ping() (bool, error) {
conn := s.Cluster.Get()
defer conn.Close()
data, err := conn.Do("PING")
if err != nil || data == nil {
res := s.Rdb.Ping(context.Background())
if result, err := res.Result(); result != "PONG" || err != nil {
return false, err
}
return data == "PONG", nil
return true, nil
}

func (s *Store) delete(session *sessions.Session) error {
conn := s.Cluster.Get()
defer conn.Close()
if _, err := conn.Do("DEL", s.keyPrefix+session.ID); err != nil {
return err
}
return nil
del := s.Rdb.Del(context.Background(), s.keyPrefix+session.ID)
return del.Err()
}

// LoadSessionBySessionId Get session using session_id even without a context
Expand All @@ -257,54 +237,16 @@ func SaveSessionWithoutContext(s *Store, sessionId string, session *sessions.Ses
return s.save(session)
}

// CreateDefaultPool return a function is used to set redisc.Cluster CreatePool by default
func CreateDefaultPool(maxIdle int, network string, password ...string) func(address string, options ...redis.DialOption) (*redis.Pool, error) {
return func(address string, options ...redis.DialOption) (*redis.Pool, error) {
var pwd string
if len(password) > 0 {
pwd = password[0]
}
pool := &redis.Pool{
MaxIdle: maxIdle,
IdleTimeout: 240 * time.Second,
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
Dial: func() (redis.Conn, error) {
return dial(network, address, pwd)
},
}
conn := pool.Get()
defer conn.Close()
data, err := conn.Do("PING")
if err != nil || data == nil {
return nil, err
}
return pool, nil
}
}

func newCluster(
startupNodes []string,
createPoolFunc func(address string, options ...redis.DialOption) (*redis.Pool, error),
) *redisc.Cluster {
return &redisc.Cluster{
StartupNodes: startupNodes,
CreatePool: createPoolFunc,
}
}

func dial(network, address, password string) (redis.Conn, error) {
c, err := redis.Dial(network, address)
if err != nil {
return nil, err
}
if password != "" {
if _, err := c.Do("AUTH", password); err != nil {
c.Close()
return nil, err
}
func newOption(
addrs []string,
password string,
maxIdleConns int,
newClient func(opt *gredis.Options) *gredis.Client,
) *gredis.ClusterOptions {
return &gredis.ClusterOptions{
Addrs: addrs,
NewClient: newClient,
Password: password,
MaxIdleConns: maxIdleConns,
}
return c, err
}
Loading

0 comments on commit afa1607

Please sign in to comment.