github.com/kaydxh/golang@v0.0.131/pkg/database/redis/redis.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package redis 23 24 import ( 25 "context" 26 "fmt" 27 "time" 28 29 "github.com/go-redis/redis/v8" 30 31 time_ "github.com/kaydxh/golang/go/time" 32 ) 33 34 var redisDB RedisDB 35 36 // Default values for Redis. 37 const ( 38 DefaultMinIdleConns = 10 39 DefaultPoolSize = 10 40 DefaultDialTimeout = 5 * time.Second 41 DefaultReadTimeout = 5 * time.Second 42 DefaultWriteTimeout = 5 * time.Second 43 DefaultMasterName = "mymaster" 44 ) 45 46 type DBConfig struct { 47 Addresses []string 48 UserName string 49 Password string 50 DB int 51 } 52 53 type RedisClient struct { 54 Conf DBConfig 55 db *redis.Client 56 57 opts struct { 58 poolSize int 59 minIdleConns int 60 dialTimeout time.Duration 61 readTimeout time.Duration 62 writeTimeout time.Duration 63 masterName string 64 } 65 } 66 67 func NewRedisClient(conf DBConfig, opts ...RedisOption) *RedisClient { 68 c := &RedisClient{ 69 Conf: conf, 70 } 71 c.opts.poolSize = DefaultPoolSize 72 c.opts.minIdleConns = DefaultMinIdleConns 73 c.opts.dialTimeout = DefaultDialTimeout 74 c.opts.readTimeout = DefaultReadTimeout 75 c.opts.writeTimeout = DefaultWriteTimeout 76 c.opts.masterName = DefaultMasterName 77 78 c.ApplyOptions(opts...) 79 80 return c 81 } 82 83 func GetDB() *redis.Client { 84 return redisDB.Load() 85 } 86 87 func (r *RedisClient) GetRedis(ctx context.Context) (*redis.Client, error) { 88 if r.db != nil { 89 return r.db, nil 90 } 91 92 if len(r.Conf.Addresses) == 0 { 93 return nil, fmt.Errorf("invalid redis address") 94 } 95 96 var db *redis.Client 97 if len(r.Conf.Addresses) == 1 { 98 db = redis.NewClient(&redis.Options{ 99 Addr: r.Conf.Addresses[0], 100 Password: r.Conf.Password, 101 DB: r.Conf.DB, 102 PoolSize: r.opts.poolSize, 103 MinIdleConns: r.opts.minIdleConns, 104 DialTimeout: r.opts.dialTimeout, 105 ReadTimeout: r.opts.readTimeout, 106 WriteTimeout: r.opts.writeTimeout, 107 }) 108 } 109 110 if len(r.Conf.Addresses) > 1 { 111 db = redis.NewFailoverClient(&redis.FailoverOptions{ 112 MasterName: r.opts.masterName, 113 SentinelAddrs: r.Conf.Addresses, 114 Password: r.Conf.Password, 115 DB: r.Conf.DB, 116 PoolSize: r.opts.poolSize, 117 MinIdleConns: r.opts.minIdleConns, 118 DialTimeout: r.opts.dialTimeout, 119 ReadTimeout: r.opts.readTimeout, 120 WriteTimeout: r.opts.writeTimeout, 121 }) 122 } 123 _, err := db.Ping(ctx).Result() 124 if err != nil { 125 return nil, err 126 } 127 128 r.db = db 129 redisDB.Store(db) 130 131 return r.db, nil 132 } 133 134 func (r *RedisClient) GetDatabaseUntil( 135 ctx context.Context, 136 maxWaitInterval time.Duration, failAfter time.Duration) (*redis.Client, error) { 137 138 var db *redis.Client 139 exp := time_.NewExponentialBackOff( 140 time_.WithExponentialBackOffOptionMaxInterval(maxWaitInterval), 141 time_.WithExponentialBackOffOptionMaxElapsedTime(failAfter), 142 ) 143 err := time_.BackOffUntilWithContext(ctx, func(ctx context.Context) (err_ error) { 144 db, err_ = r.GetRedis(ctx) 145 if err_ != nil { 146 return err_ 147 } 148 return nil 149 }, exp, true, false) 150 if err != nil { 151 return nil, fmt.Errorf("get database fail after: %v", failAfter) 152 } 153 154 return db, nil 155 156 } 157 158 func (r *RedisClient) Close() error { 159 if r.db == nil { 160 return fmt.Errorf("no redis client") 161 } 162 return r.db.Close() 163 }