mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-02 16:30:17 +02:00
redis: add redis cluster support (#1992)
* redis: add redis cluster support * redis: update docs
This commit is contained in:
parent
0b1e89925a
commit
77fe37c8c0
9 changed files with 386 additions and 12 deletions
|
@ -10,16 +10,47 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/scylladb/go-set"
|
||||
)
|
||||
|
||||
func newClientFromURL(rawurl string, tlsConfig *tls.Config) (*redis.Client, error) {
|
||||
var (
|
||||
standardSchemes = set.NewStringSet("redis", "rediss", "unix")
|
||||
clusterSchemes = set.NewStringSet(
|
||||
"redis+cluster", "redis-cluster",
|
||||
"rediss+cluster", "rediss-cluster",
|
||||
"redis+clusters", "redis-clusters",
|
||||
)
|
||||
sentinelSchemes = set.NewStringSet(
|
||||
"redis+sentinel", "redis-sentinel",
|
||||
"rediss+sentinel", "rediss-sentinel",
|
||||
"redis+sentinels", "redis-sentinels",
|
||||
)
|
||||
sentinelClusterSchemes = set.NewStringSet(
|
||||
"redis+sentinel+cluster", "redis-sentinel-cluster",
|
||||
"rediss+sentinel+cluster", "rediss-sentinel-cluster",
|
||||
"redis+sentinels+cluster", "redis-sentinels-cluster",
|
||||
"redis+sentinel+clusters", "redis-sentinel-clusters",
|
||||
)
|
||||
tlsSchemes = set.NewStringSet(
|
||||
"rediss",
|
||||
"rediss+cluster", "rediss-cluster",
|
||||
"redis+clusters", "redis-clusters",
|
||||
"rediss+sentinel", "rediss-sentinel",
|
||||
"redis+sentinels", "redis-sentinels",
|
||||
"rediss+sentinel+cluster", "rediss-sentinel-cluster",
|
||||
"redis+sentinels+cluster", "redis-sentinels-cluster",
|
||||
"redis+sentinel+clusters", "redis-sentinel-clusters",
|
||||
)
|
||||
)
|
||||
|
||||
func newClientFromURL(rawurl string, tlsConfig *tls.Config) (redis.UniversalClient, error) {
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "redis", "rediss", "unix":
|
||||
switch {
|
||||
case standardSchemes.Has(u.Scheme):
|
||||
opts, err := redis.ParseURL(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -30,7 +61,17 @@ func newClientFromURL(rawurl string, tlsConfig *tls.Config) (*redis.Client, erro
|
|||
}
|
||||
return redis.NewClient(opts), nil
|
||||
|
||||
case "redis-sentinel", "rediss-sentinel", "redis-sentinels":
|
||||
case clusterSchemes.Has(u.Scheme):
|
||||
opts, err := ParseClusterURL(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.TLSConfig != nil {
|
||||
opts.TLSConfig = tlsConfig
|
||||
}
|
||||
return redis.NewClusterClient(opts), nil
|
||||
|
||||
case sentinelSchemes.Has(u.Scheme):
|
||||
opts, err := ParseSentinelURL(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -40,16 +81,129 @@ func newClientFromURL(rawurl string, tlsConfig *tls.Config) (*redis.Client, erro
|
|||
}
|
||||
return redis.NewFailoverClient(opts), nil
|
||||
|
||||
case sentinelClusterSchemes.Has(u.Scheme):
|
||||
opts, err := ParseSentinelURL(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.TLSConfig != nil {
|
||||
opts.TLSConfig = tlsConfig
|
||||
}
|
||||
return redis.NewFailoverClusterClient(opts), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported URL scheme: %s", u.Scheme)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseClusterURL parses a redis-cluster URL. Format is:
|
||||
//
|
||||
// redis+cluster://[username:password@]host:port[,host2:port2,...]/[?param1=value1[¶m2=value=2&...]]
|
||||
//
|
||||
// Additionally TLS is supported with rediss+cluster, or redis+clusters. Supported query params:
|
||||
//
|
||||
// max_redirects: int
|
||||
// read_only: bool
|
||||
// route_by_latency: bool
|
||||
// route_randomly: bool
|
||||
// max_retries: int
|
||||
// min_retry_backoff: duration
|
||||
// max_retry_backoff: duration
|
||||
// dial_timeout: duration
|
||||
// read_timeout: duration
|
||||
// write_timeout: duration
|
||||
// pool_size: int
|
||||
// min_idle_conns: int
|
||||
// max_conn_age: duration
|
||||
// pool_timeout: duration
|
||||
// idle_timeout: duration
|
||||
// idle_check_frequency: duration
|
||||
//
|
||||
func ParseClusterURL(rawurl string) (*redis.ClusterOptions, error) {
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := new(redis.ClusterOptions)
|
||||
|
||||
hostParts := strings.Split(u.Host, ",")
|
||||
for _, hostPart := range hostParts {
|
||||
host, port, err := net.SplitHostPort(hostPart)
|
||||
if err != nil {
|
||||
host = hostPart
|
||||
port = "6379"
|
||||
}
|
||||
opts.Addrs = append(opts.Addrs,
|
||||
net.JoinHostPort(host, port))
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
if err := parseIntParam(&opts.MaxRedirects, q, "max_redirects"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseBoolParam(&opts.ReadOnly, q, "read_only"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseBoolParam(&opts.RouteByLatency, q, "route_by_latency"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseBoolParam(&opts.RouteRandomly, q, "route_randomly"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ui := u.User; ui != nil {
|
||||
opts.Username = ui.Username()
|
||||
opts.Password, _ = ui.Password()
|
||||
}
|
||||
if err := parseIntParam(&opts.MaxRetries, q, "max_retries"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseDurationParam(&opts.MinRetryBackoff, q, "min_retry_backoff"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseDurationParam(&opts.MaxRetryBackoff, q, "max_retry_backoff"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseDurationParam(&opts.DialTimeout, q, "dial_timeout"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseDurationParam(&opts.ReadTimeout, q, "read_timeout"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseDurationParam(&opts.WriteTimeout, q, "write_timeout"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseIntParam(&opts.PoolSize, q, "pool_size"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseIntParam(&opts.MinIdleConns, q, "min_idle_conns"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseDurationParam(&opts.MaxConnAge, q, "max_conn_age"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseDurationParam(&opts.PoolTimeout, q, "pool_timeout"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseDurationParam(&opts.IdleTimeout, q, "idle_timeout"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseDurationParam(&opts.IdleCheckFrequency, q, "idle_check_frequency"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tlsSchemes.Has(u.Scheme) {
|
||||
opts.TLSConfig = &tls.Config{} //nolint
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// ParseSentinelURL parses a redis-sentinel URL. Format is based on https://github.com/exponea/redis-sentinel-url:
|
||||
//
|
||||
// redis+sentinel://[:password@]host:port[,host2:port2,...][/service_name[/db]][?param1=value1[¶m2=value=2&...]]
|
||||
//
|
||||
// Additionally TLS is supported with rediss-sentinel, or redis-sentinels. Supported query params:
|
||||
// Additionally TLS is supported with rediss+sentinel, or redis+sentinels. Supported query params:
|
||||
//
|
||||
// slave_only: bool
|
||||
// use_disconnected_slaves: bool
|
||||
|
@ -107,6 +261,12 @@ func ParseSentinelURL(rawurl string) (*redis.FailoverOptions, error) {
|
|||
if err := parseBoolParam(&opts.SlaveOnly, q, "slave_only"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseBoolParam(&opts.RouteByLatency, q, "route_by_latency"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseBoolParam(&opts.RouteRandomly, q, "route_randomly"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := parseBoolParam(&opts.UseDisconnectedSlaves, q, "use_disconnected_slaves"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -152,7 +312,7 @@ func ParseSentinelURL(rawurl string) (*redis.FailoverOptions, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if u.Scheme == "rediss-sentinel" || u.Scheme == "redis-sentinels" {
|
||||
if tlsSchemes.Has(u.Scheme) {
|
||||
opts.TLSConfig = &tls.Config{} //nolint
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue