github.com/livekit/protocol@v1.16.1-0.20240517185851-47e4c6bba773/redis/redis.go (about) 1 // Copyright 2023 LiveKit, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package redis 16 17 import ( 18 "context" 19 "crypto/tls" 20 "time" 21 22 "github.com/livekit/protocol/xtls" 23 "github.com/pkg/errors" 24 "github.com/redis/go-redis/v9" 25 26 "github.com/livekit/protocol/logger" 27 ) 28 29 var ErrNotConfigured = errors.New("Redis is not configured") 30 31 type RedisConfig struct { 32 Address string `yaml:"address,omitempty"` 33 Username string `yaml:"username,omitempty"` 34 Password string `yaml:"password,omitempty"` 35 DB int `yaml:"db,omitempty"` 36 // Deprecated: use TLS instead of UseTLS 37 UseTLS bool `yaml:"use_tls,omitempty"` 38 TLS *xtls.Config `yaml:"tls,omitempty"` 39 MasterName string `yaml:"sentinel_master_name,omitempty"` 40 SentinelUsername string `yaml:"sentinel_username,omitempty"` 41 SentinelPassword string `yaml:"sentinel_password,omitempty"` 42 SentinelAddresses []string `yaml:"sentinel_addresses,omitempty"` 43 ClusterAddresses []string `yaml:"cluster_addresses,omitempty"` 44 DialTimeout int `yaml:"dial_timeout,omitempty"` 45 ReadTimeout int `yaml:"read_timeout,omitempty"` 46 WriteTimeout int `yaml:"write_timeout,omitempty"` 47 // for clustererd mode only, number of redirects to follow, defaults to 2 48 MaxRedirects *int `yaml:"max_redirects,omitempty"` 49 PoolTimeout time.Duration `yaml:"pool_timeout,omitempty"` 50 PoolSize int `yaml:"pool_size,omitempty"` 51 } 52 53 func (r *RedisConfig) IsConfigured() bool { 54 if r.Address != "" { 55 return true 56 } 57 if len(r.SentinelAddresses) > 0 { 58 return true 59 } 60 if len(r.ClusterAddresses) > 0 { 61 return true 62 } 63 return false 64 } 65 66 func (r *RedisConfig) GetMaxRedirects() int { 67 if r.MaxRedirects != nil { 68 return *r.MaxRedirects 69 } 70 return 2 71 } 72 73 func GetRedisClient(conf *RedisConfig) (redis.UniversalClient, error) { 74 if conf == nil { 75 return nil, nil 76 } 77 78 if !conf.IsConfigured() { 79 return nil, ErrNotConfigured 80 } 81 82 var rcOptions *redis.UniversalOptions 83 var rc redis.UniversalClient 84 var tlsConfig *tls.Config 85 86 if conf.TLS != nil && conf.TLS.Enabled { 87 var err error 88 tlsConfig, err = conf.TLS.ClientTLSConfig() 89 if err != nil { 90 return nil, err 91 } 92 } else if conf.UseTLS { 93 tlsConfig = &tls.Config{ 94 MinVersion: tls.VersionTLS12, 95 } 96 } 97 98 if len(conf.SentinelAddresses) > 0 { 99 logger.Infow("connecting to redis", "sentinel", true, "addr", conf.SentinelAddresses, "masterName", conf.MasterName) 100 101 // By default DialTimeout set to 2s 102 if conf.DialTimeout == 0 { 103 conf.DialTimeout = 2000 104 } 105 // By default ReadTimeout set to 0.2s 106 if conf.ReadTimeout == 0 { 107 conf.ReadTimeout = 200 108 } 109 // By default WriteTimeout set to 0.2s 110 if conf.WriteTimeout == 0 { 111 conf.WriteTimeout = 200 112 } 113 114 rcOptions = &redis.UniversalOptions{ 115 Addrs: conf.SentinelAddresses, 116 SentinelUsername: conf.SentinelUsername, 117 SentinelPassword: conf.SentinelPassword, 118 MasterName: conf.MasterName, 119 Username: conf.Username, 120 Password: conf.Password, 121 DB: conf.DB, 122 TLSConfig: tlsConfig, 123 DialTimeout: time.Duration(conf.DialTimeout) * time.Millisecond, 124 ReadTimeout: time.Duration(conf.ReadTimeout) * time.Millisecond, 125 WriteTimeout: time.Duration(conf.WriteTimeout) * time.Millisecond, 126 PoolTimeout: conf.PoolTimeout, 127 PoolSize: conf.PoolSize, 128 } 129 } else if len(conf.ClusterAddresses) > 0 { 130 logger.Infow("connecting to redis", "cluster", true, "addr", conf.ClusterAddresses) 131 rcOptions = &redis.UniversalOptions{ 132 Addrs: conf.ClusterAddresses, 133 Username: conf.Username, 134 Password: conf.Password, 135 DB: conf.DB, 136 TLSConfig: tlsConfig, 137 MaxRedirects: conf.GetMaxRedirects(), 138 PoolTimeout: conf.PoolTimeout, 139 PoolSize: conf.PoolSize, 140 } 141 } else { 142 logger.Infow("connecting to redis", "simple", true, "addr", conf.Address) 143 rcOptions = &redis.UniversalOptions{ 144 Addrs: []string{conf.Address}, 145 Username: conf.Username, 146 Password: conf.Password, 147 DB: conf.DB, 148 TLSConfig: tlsConfig, 149 PoolTimeout: conf.PoolTimeout, 150 PoolSize: conf.PoolSize, 151 } 152 } 153 rc = redis.NewUniversalClient(rcOptions) 154 155 if err := rc.Ping(context.Background()).Err(); err != nil { 156 err = errors.Wrap(err, "unable to connect to redis") 157 return nil, err 158 } 159 160 return rc, nil 161 }