github.com/anycable/anycable-go@v1.5.1/redis/config.go (about) 1 package redis 2 3 import ( 4 "net/url" 5 "strings" 6 "time" 7 8 "github.com/redis/rueidis" 9 ) 10 11 // RedisConfig contains Redis pubsub adapter configuration 12 type RedisConfig struct { 13 // Redis instance URL or master name in case of sentinels usage 14 // or list of URLs if cluster usage 15 URL string 16 // Redis channel to subscribe to (legacy pub/sub) 17 Channel string 18 // Redis stream consumer group name 19 Group string 20 // Redis stream read wait time in milliseconds 21 StreamReadBlockMilliseconds int64 22 // Internal channel name for node-to-node broadcasting 23 InternalChannel string 24 // List of Redis Sentinel addresses 25 Sentinels string 26 // Redis Sentinel discovery interval (seconds) 27 SentinelDiscoveryInterval int 28 // Redis keepalive ping interval (seconds) 29 KeepalivePingInterval int 30 // Whether to check server's certificate for validity (in case of rediss:// protocol) 31 TLSVerify bool 32 // Max number of reconnect attempts 33 MaxReconnectAttempts int 34 // Disable client-side caching 35 DisableCache bool 36 37 // List of hosts to connect 38 hosts []string 39 // Sentinel Master host to connect 40 sentinelMaster string 41 } 42 43 // NewRedisConfig builds a new config for Redis pubsub 44 func NewRedisConfig() RedisConfig { 45 return RedisConfig{ 46 KeepalivePingInterval: 30, 47 URL: "redis://localhost:6379", 48 Channel: "__anycable__", 49 Group: "bx", 50 StreamReadBlockMilliseconds: 2000, 51 InternalChannel: "__anycable_internal__", 52 SentinelDiscoveryInterval: 30, 53 TLSVerify: false, 54 MaxReconnectAttempts: 5, 55 DisableCache: false, 56 } 57 } 58 59 func (config *RedisConfig) IsCluster() bool { 60 return len(config.hosts) > 1 && !config.IsSentinel() 61 } 62 63 func (config *RedisConfig) IsSentinel() bool { 64 return config.Sentinels != "" || config.sentinelMaster != "" 65 } 66 67 func (config *RedisConfig) Hostnames() []string { 68 return config.hosts 69 } 70 71 func (config *RedisConfig) Hostname() string { 72 if config.IsSentinel() { 73 return config.sentinelMaster 74 } else { 75 return config.hosts[0] 76 } 77 } 78 79 func (config *RedisConfig) ToRueidisOptions() (options *rueidis.ClientOption, err error) { 80 if config.IsSentinel() { 81 options, err = config.parseSentinels() 82 } else { 83 options, err = parseRedisURL(config.URL) 84 } 85 86 if err != nil { 87 return nil, err 88 } 89 90 config.hosts = options.InitAddress 91 config.sentinelMaster = options.Sentinel.MasterSet 92 93 options.Dialer.KeepAlive = time.Duration(config.KeepalivePingInterval) * time.Second 94 95 options.ShuffleInit = config.IsCluster() 96 97 if options.TLSConfig != nil { 98 options.TLSConfig.InsecureSkipVerify = !config.TLSVerify 99 } 100 101 options.DisableCache = config.DisableCache 102 103 return options, nil 104 } 105 106 func (config *RedisConfig) parseSentinels() (*rueidis.ClientOption, error) { 107 sentinelMaster, err := url.Parse(config.URL) 108 109 if err != nil { 110 return nil, err 111 } 112 113 options, err := parseRedisURL(config.Sentinels) 114 115 if err != nil { 116 return nil, err 117 } 118 119 options.Sentinel.MasterSet = sentinelMaster.Host 120 121 return options, nil 122 } 123 124 func parseRedisURL(url string) (options *rueidis.ClientOption, err error) { 125 urls := strings.Split(url, ",") 126 127 for _, addr := range urls { 128 addr = chompTrailingSlashHostname(addr) 129 130 currentOptions, err := rueidis.ParseURL(ensureRedisScheme(addr)) 131 132 if err != nil { 133 return nil, err 134 } 135 136 if options == nil { 137 options = ¤tOptions 138 } else { 139 options.InitAddress = append(options.InitAddress, currentOptions.InitAddress...) 140 } 141 } 142 143 return options, nil 144 } 145 146 // TODO: upstream this change to `rueidis` URL parsing 147 // as the implementation doesn't tolerate the trailing slash hostnames (`redis-cli` does). 148 func chompTrailingSlashHostname(url string) string { 149 return strings.TrimSuffix(url, "/") 150 } 151 152 func ensureRedisScheme(url string) string { 153 if strings.Contains(url, "://") { 154 return url 155 } 156 157 return "redis://" + url 158 }