github.com/polarismesh/polaris@v1.17.8/common/redispool/config.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package redispool 19 20 import ( 21 "crypto/tls" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "time" 26 27 "github.com/go-redis/redis/v8" 28 "github.com/mitchellh/mapstructure" 29 ) 30 31 const ( 32 // redisStandalone 单机模式 33 redisStandalone = "standalone" 34 // redisSentinel 哨兵模式 35 redisSentinel = "sentinel" 36 // redisCluster 集群模式 37 redisCluster = "cluster" 38 ) 39 40 // Config redis pool configuration 41 type Config struct { 42 // DeployMode is the run mode of the redis pool, support `standalone`、`cluster`、`sentinel`、or `ckv` 43 DeployMode string `json:"deployMode"` 44 // StandaloneConfig standalone-deploy-mode config 45 StandaloneConfig 46 // StandaloneConfig sentinel-deploy-mode config 47 SentinelConfig 48 // ClusterConfig cluster-deploy-mode config 49 ClusterConfig 50 } 51 52 // UnmarshalJSON unmarshal config from json 53 func (c *Config) UnmarshalJSON(data []byte) error { 54 var configmap map[string]interface{} 55 if err := json.Unmarshal(data, &configmap); err != nil { 56 return err 57 } 58 // 需要以用户的配置为优先 59 raw := DefaultConfig().StandaloneConfig 60 decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 61 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 62 ZeroFields: false, 63 Result: &raw, 64 TagName: "json", 65 }) 66 if err != nil { 67 return err 68 } 69 if err = decoder.Decode(configmap); err != nil { 70 return err 71 } 72 c.DeployMode, _ = configmap["deployMode"].(string) 73 c.StandaloneConfig = raw 74 switch c.DeployMode { 75 case redisCluster: 76 var clusterConfig ClusterConfig 77 if err = json.Unmarshal(data, &clusterConfig); err != nil { 78 return fmt.Errorf("unmarshal redis cluster config error: %w", err) 79 } 80 c.ClusterConfig = clusterConfig 81 case redisSentinel: 82 var sentinelConfig SentinelConfig 83 if err = json.Unmarshal(data, &sentinelConfig); err != nil { 84 return fmt.Errorf("unmarshal redis sentinel config error: %w", err) 85 } 86 c.SentinelConfig = sentinelConfig 87 case redisStandalone: 88 default: 89 } 90 return nil 91 } 92 93 // StandaloneOptions standalone-mode options 94 func (c *Config) StandaloneOptions() *redis.Options { 95 redisOption := &redis.Options{ 96 Addr: c.KvAddr, 97 Username: c.KvUser, 98 Password: c.KvPasswd, 99 MaxRetries: c.MaxRetries, 100 DialTimeout: c.ConnectTimeout, 101 PoolSize: c.PoolSize, 102 MinIdleConns: c.MinIdleConns, 103 IdleTimeout: c.IdleTimeout, 104 DB: c.DB, 105 ReadTimeout: c.ReadTimeout, 106 WriteTimeout: c.WriteTimeout, 107 PoolTimeout: c.PoolTimeout, 108 MaxConnAge: c.MaxConnAge, 109 } 110 111 if redisOption.ReadTimeout == 0 { 112 redisOption.ReadTimeout = c.MsgTimeout 113 } 114 115 if redisOption.WriteTimeout == 0 { 116 redisOption.WriteTimeout = c.MsgTimeout 117 } 118 119 if c.MaxConnAge == 0 { 120 redisOption.MaxConnAge = 1800 * time.Second 121 } 122 123 if c.WithTLS { 124 redisOption.TLSConfig = c.tlsConfig 125 if redisOption.TLSConfig == nil { 126 redisOption.TLSConfig = &tls.Config{ 127 MinVersion: tls.VersionTLS12, 128 } 129 } 130 } 131 return redisOption 132 } 133 134 // ClusterOptions cluster-mode client options 135 func (c *Config) ClusterOptions() *redis.ClusterOptions { 136 standalone := c.StandaloneOptions() 137 return &redis.ClusterOptions{ 138 Addrs: c.ClusterConfig.Addrs, 139 140 RouteByLatency: c.ClusterConfig.RouteByLatency, 141 RouteRandomly: c.ClusterConfig.RouteRandomly, 142 143 Username: standalone.Username, 144 Password: standalone.Password, 145 MaxRetries: standalone.MaxRetries, 146 DialTimeout: standalone.DialTimeout, 147 PoolSize: standalone.PoolSize, 148 MinIdleConns: standalone.MinIdleConns, 149 IdleTimeout: standalone.IdleTimeout, 150 ReadTimeout: standalone.ReadTimeout, 151 WriteTimeout: standalone.WriteTimeout, 152 PoolTimeout: standalone.PoolTimeout, 153 MaxConnAge: standalone.MaxConnAge, 154 TLSConfig: standalone.TLSConfig, 155 } 156 } 157 158 // FailOverOptions sentinel-model options 159 func (c *Config) FailOverOptions() *redis.FailoverOptions { 160 standalone := c.StandaloneOptions() 161 return &redis.FailoverOptions{ 162 SentinelAddrs: c.SentinelConfig.Addrs, 163 MasterName: c.SentinelConfig.MasterName, 164 165 SentinelUsername: c.SentinelConfig.SentinelUsername, 166 SentinelPassword: c.SentinelConfig.SentinelPassword, 167 168 Username: standalone.Username, 169 Password: standalone.Password, 170 MaxRetries: standalone.MaxRetries, 171 DialTimeout: standalone.DialTimeout, 172 PoolSize: standalone.PoolSize, 173 MinIdleConns: standalone.MinIdleConns, 174 IdleTimeout: standalone.IdleTimeout, 175 ReadTimeout: standalone.ReadTimeout, 176 WriteTimeout: standalone.WriteTimeout, 177 PoolTimeout: standalone.PoolTimeout, 178 MaxConnAge: standalone.MaxConnAge, 179 TLSConfig: standalone.TLSConfig, 180 } 181 } 182 183 // StandaloneConfig redis pool basic-configuration, also used as sentinel/cluster common config. 184 type StandaloneConfig struct { 185 // KvAddr is the address of the redis server 186 KvAddr string `json:"kvAddr"` 187 188 // Use the specified Username to authenticate the current connection 189 // with one of the connections defined in the ACL list when connecting 190 // to a Redis 6.0 instance, or greater, that is using the Redis ACL system. 191 KvUser string `json:"kvUser"` 192 193 // KvPasswd for go-redis password or username (redis 6.0 version) 194 // Optional password. Must match the password specified in the 195 // requirepass server configuration option (if connecting to a Redis 5.0 instance, or lower), 196 // or the User Password when connecting to a Redis 6.0 instance, or greater, 197 // that is using the Redis ACL system. 198 KvPasswd string `json:"kvPasswd"` 199 200 // Minimum number of idle connections which is useful when establishing 201 // new connection is slow. 202 MinIdleConns int `json:"minIdleConns"` 203 204 // Amount of time after which client closes idle connections. 205 // Should be less than server's timeout. 206 // Default is 5 minutes. -1 disables idle timeout check. 207 IdleTimeout time.Duration `json:"idleTimeout"` 208 209 // ConnectTimeout for go-redis is Dial timeout for establishing new connections. 210 // Default is 5 seconds. 211 ConnectTimeout time.Duration `json:"connectTimeout"` 212 213 MsgTimeout time.Duration `json:"msgTimeout"` 214 Concurrency int `json:"concurrency"` 215 Compatible bool `json:"compatible"` 216 MaxRetry int `json:"maxRetry"` 217 MinBatchCount int `json:"minBatchCount"` 218 WaitTime time.Duration `json:"waitTime"` 219 220 // MaxRetries is Maximum number of retries before giving up. 221 // Default is 3 retries; -1 (not 0) disables retries. 222 MaxRetries int `json:"maxRetries"` 223 224 // DB is Database to be selected after connecting to the server. 225 DB int `json:"DB"` 226 227 // ReadTimeout for socket reads. If reached, commands will fail 228 // with a timeout instead of blocking. Use value -1 for no timeout and 0 for default. 229 // Default is 3 seconds. 230 ReadTimeout time.Duration `json:"readTimeout"` 231 232 // WriteTimeout for socket writes. If reached, commands will fail 233 // with a timeout instead of blocking. 234 // Default is ReadTimeout. 235 WriteTimeout time.Duration `json:"writeTimeout"` 236 237 // Maximum number of socket connections. 238 // Default is 10 connections per every available CPU as reported by runtime.GOMAXPROCS. 239 PoolSize int `json:"poolSize" mapstructure:"poolSize"` 240 241 // Amount of time client waits for connection if all connections 242 // are busy before returning an error. 243 // Default is ReadTimeout + 1 second. 244 PoolTimeout time.Duration `json:"poolTimeout"` 245 246 // Connection age at which client retires (closes) the connection. 247 // Default is to not close aged connections. 248 MaxConnAge time.Duration `json:"maxConnAge"` 249 250 // WithTLS whether open TLSConfig 251 // if WithTLS is true, you should call WithEnableWithTLS,and then TLSConfig is not should be nil 252 // In this case you should call WithTLSConfig func to set tlsConfig 253 WithTLS bool `json:"withTLS"` 254 255 // TLS Config to use. When set TLS will be negotiated. 256 tlsConfig *tls.Config 257 } 258 259 // SentinelConfig sentinel pool configuration. 260 // See github.com/go-redis/redis/v8/redis.FailoverOptions 261 type SentinelConfig struct { 262 // MasterName is the name of the master instance 263 MasterName string `json:"masterName"` 264 265 // A seed list of host:port addresses of sentinel servers. 266 // Use shor name, to keep in line with ClusterConfig.Addrs 267 Addrs []string `json:"addrs"` 268 269 // Username ACL User and Password 270 SentinelUsername string `json:"sentinelUsername"` 271 // Password ACL User and Password 272 SentinelPassword string `json:"sentinelPassword"` 273 274 // Route all commands to slave read-only nodes. 275 SlaveOnly bool 276 277 // Use slaves disconnected with master when cannot get connected slaves 278 // Now, this option only works in RandomSlaveAddr function. 279 UseDisconnectedSlaves bool 280 } 281 282 // ClusterConfig redis cluster pool configuration 283 // See github.com/go-redis/redis/v8/redis.ClusterOptions 284 type ClusterConfig struct { 285 // A seed list of host:port addresses of cluster nodes. 286 Addrs []string 287 288 // Enables read-only commands on slave nodes. 289 ReadOnly bool 290 291 // Allows routing read-only commands to the closest master or slave node. 292 // It automatically enables ReadOnly. 293 RouteByLatency bool 294 295 // Allows routing read-only commands to the random master or slave node. 296 // It automatically enables ReadOnly. 297 RouteRandomly bool 298 } 299 300 // DefaultConfig redis pool configuration with default values 301 func DefaultConfig() *Config { 302 return &Config{ 303 StandaloneConfig: StandaloneConfig{ 304 PoolSize: 200, 305 MinIdleConns: 30, 306 IdleTimeout: 120 * time.Second, 307 ConnectTimeout: 300 * time.Millisecond, 308 MsgTimeout: 300 * time.Millisecond, 309 Concurrency: 200, 310 Compatible: false, 311 MaxRetry: 2, 312 MinBatchCount: 10, 313 WaitTime: 50 * time.Millisecond, 314 DB: 0, 315 PoolTimeout: 3 * time.Second, 316 MaxConnAge: 1800 * time.Second, 317 }, 318 } 319 } 320 321 // Validate validate config params 322 func (c *Config) Validate() error { 323 if len(c.KvAddr) == 0 { 324 return errors.New("kvAddr is empty") 325 } 326 // password is required only when ACL's user is given 327 if len(c.KvUser) > 0 && len(c.KvPasswd) == 0 { 328 return errors.New("kvPasswd is empty") 329 } 330 if c.MinIdleConns <= 0 { 331 return errors.New("minIdleConns is empty") 332 } 333 if c.PoolSize <= 0 { 334 return errors.New("poolSize is empty") 335 } 336 if c.IdleTimeout == 0 { 337 return errors.New("idleTimeout is empty") 338 } 339 if c.ConnectTimeout == 0 { 340 return errors.New("connectTimeout is empty") 341 } 342 if c.MsgTimeout == 0 { 343 return errors.New("msgTimeout is empty") 344 } 345 if c.Concurrency <= 0 { 346 return errors.New("concurrency is empty") 347 } 348 if c.MaxRetry < 0 { 349 return errors.New("maxRetry is empty") 350 } 351 352 if c.DeployMode == redisSentinel { 353 if len(c.SentinelConfig.Addrs) == 0 { 354 return errors.New("sentinel address list is empty") 355 } 356 if c.SentinelConfig.SentinelUsername != "" && c.SentinelConfig.SentinelPassword == "" { 357 return errors.New("sentinel acl username or password is empty") 358 } 359 } 360 361 if c.DeployMode == redisCluster && len(c.ClusterConfig.Addrs) == 0 { 362 return errors.New("cluster address list is empty") 363 } 364 return nil 365 }