package redisimport ()// Limiter is the interface of a rate limiter or a circuit breaker.typeLimiterinterface {// Allow returns nil if operation is allowed or an error otherwise. // If operation is allowed client must ReportResult of the operation // whether it is a success or a failure.Allow() error// ReportResult reports the result of the previously allowed operation. // nil indicates a success, non-nil error usually indicates a failure.ReportResult(result error)}// Options keeps the settings to set up redis connection.typeOptionsstruct {// The network type, either tcp or unix. // Default is tcp. Network string// host:port address. Addr string// ClientName will execute the `CLIENT SETNAME ClientName` command for each conn. ClientName string// Dialer creates new network connection and has priority over // Network and Addr options. Dialer func(ctx context.Context, network, addr string) (net.Conn, error)// Hook that is called when new connection is established. OnConnect func(ctx context.Context, cn *Conn) error// Use the specified Username to authenticate the current connection // with one of the connections defined in the ACL list when connecting // to a Redis 6.0 instance, or greater, that is using the Redis ACL system. Username string// Optional password. Must match the password specified in the // requirepass server configuration option (if connecting to a Redis 5.0 instance, or lower), // or the User Password when connecting to a Redis 6.0 instance, or greater, // that is using the Redis ACL system. Password string// CredentialsProvider allows the username and password to be updated // before reconnecting. It should return the current username and password. CredentialsProvider func() (username string, password string)// Database to be selected after connecting to the server. DB int// Maximum number of retries before giving up. // Default is 3 retries; -1 (not 0) disables retries. MaxRetries int// Minimum backoff between each retry. // Default is 8 milliseconds; -1 disables backoff. MinRetryBackoff time.Duration// Maximum backoff between each retry. // Default is 512 milliseconds; -1 disables backoff. MaxRetryBackoff time.Duration// Dial timeout for establishing new connections. // Default is 5 seconds. DialTimeout time.Duration// Timeout for socket reads. If reached, commands will fail // with a timeout instead of blocking. Supported values: // - `0` - default timeout (3 seconds). // - `-1` - no timeout (block indefinitely). // - `-2` - disables SetReadDeadline calls completely. ReadTimeout time.Duration// Timeout for socket writes. If reached, commands will fail // with a timeout instead of blocking. Supported values: // - `0` - default timeout (3 seconds). // - `-1` - no timeout (block indefinitely). // - `-2` - disables SetWriteDeadline calls completely. WriteTimeout time.Duration// ContextTimeoutEnabled controls whether the client respects context timeouts and deadlines. // See https://redis.uptrace.dev/guide/go-redis-debugging.html#timeouts ContextTimeoutEnabled bool// Type of connection pool. // true for FIFO pool, false for LIFO pool. // Note that FIFO has slightly higher overhead compared to LIFO, // but it helps closing idle connections faster reducing the pool size. PoolFIFO bool// Maximum number of socket connections. // Default is 10 connections per every available CPU as reported by runtime.GOMAXPROCS. PoolSize int// Amount of time client waits for connection if all connections // are busy before returning an error. // Default is ReadTimeout + 1 second. PoolTimeout time.Duration// Minimum number of idle connections which is useful when establishing // new connection is slow. MinIdleConns int// Maximum number of idle connections. MaxIdleConns int// ConnMaxIdleTime is the maximum amount of time a connection may be idle. // Should be less than server's timeout. // // Expired connections may be closed lazily before reuse. // If d <= 0, connections are not closed due to a connection's idle time. // // Default is 30 minutes. -1 disables idle timeout check. ConnMaxIdleTime time.Duration// ConnMaxLifetime is the maximum amount of time a connection may be reused. // // Expired connections may be closed lazily before reuse. // If <= 0, connections are not closed due to a connection's age. // // Default is to not close idle connections. ConnMaxLifetime time.Duration// TLS Config to use. When set, TLS will be negotiated. TLSConfig *tls.Config// Limiter interface used to implement circuit breaker or rate limiter. Limiter Limiter// Enables read only queries on slave/follower nodes. readOnly bool}func ( *Options) () {if .Addr == "" { .Addr = "localhost:6379" }if .Network == "" {ifstrings.HasPrefix(.Addr, "/") { .Network = "unix" } else { .Network = "tcp" } }if .DialTimeout == 0 { .DialTimeout = 5 * time.Second }if .Dialer == nil { .Dialer = NewDialer() }if .PoolSize == 0 { .PoolSize = 10 * runtime.GOMAXPROCS(0) }switch .ReadTimeout {case -2: .ReadTimeout = -1case -1: .ReadTimeout = 0case0: .ReadTimeout = 3 * time.Second }switch .WriteTimeout {case -2: .WriteTimeout = -1case -1: .WriteTimeout = 0case0: .WriteTimeout = .ReadTimeout }if .PoolTimeout == 0 {if .ReadTimeout > 0 { .PoolTimeout = .ReadTimeout + time.Second } else { .PoolTimeout = 30 * time.Second } }if .ConnMaxIdleTime == 0 { .ConnMaxIdleTime = 30 * time.Minute }if .MaxRetries == -1 { .MaxRetries = 0 } elseif .MaxRetries == 0 { .MaxRetries = 3 }switch .MinRetryBackoff {case -1: .MinRetryBackoff = 0case0: .MinRetryBackoff = 8 * time.Millisecond }switch .MaxRetryBackoff {case -1: .MaxRetryBackoff = 0case0: .MaxRetryBackoff = 512 * time.Millisecond }}func ( *Options) () *Options { := *return &}// NewDialer returns a function that will be used as the default dialer// when none is specified in Options.Dialer.func ( *Options) func(context.Context, string, string) (net.Conn, error) {returnfunc( context.Context, , string) (net.Conn, error) { := &net.Dialer{Timeout: .DialTimeout,KeepAlive: 5 * time.Minute, }if .TLSConfig == nil {return .DialContext(, , ) }returntls.DialWithDialer(, , , .TLSConfig) }}// ParseURL parses an URL into Options that can be used to connect to Redis.// Scheme is required.// There are two connection types: by tcp socket and by unix socket.// Tcp connection://// redis://<user>:<password>@<host>:<port>/<db_number>//// Unix connection://// unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>//// Most Option fields can be set using query parameters, with the following restrictions:// - field names are mapped using snake-case conversion: to set MaxRetries, use max_retries// - only scalar type fields are supported (bool, int, time.Duration)// - for time.Duration fields, values must be a valid input for time.ParseDuration();// additionally a plain integer as value (i.e. without unit) is intepreted as seconds// - to disable a duration field, use value less than or equal to 0; to use the default// value, leave the value blank or remove the parameter// - only the last value is interpreted if a parameter is given multiple times// - fields "network", "addr", "username" and "password" can only be set using other// URL attributes (scheme, host, userinfo, resp.), query paremeters using these// names will be treated as unknown parameters// - unknown parameter names will result in an error//// Examples://// redis://user:password@localhost:6789/3?dial_timeout=3&db=1&read_timeout=6s&max_retries=2// is equivalent to:// &Options{// Network: "tcp",// Addr: "localhost:6789",// DB: 1, // path "/3" was overridden by "&db=1"// DialTimeout: 3 * time.Second, // no time unit = seconds// ReadTimeout: 6 * time.Second,// MaxRetries: 2,// }func ( string) (*Options, error) { , := url.Parse()if != nil {returnnil, }switch .Scheme {case"redis", "rediss":returnsetupTCPConn()case"unix":returnsetupUnixConn()default:returnnil, fmt.Errorf("redis: invalid URL scheme: %s", .Scheme) }}func setupTCPConn( *url.URL) (*Options, error) { := &Options{Network: "tcp"} .Username, .Password = getUserPassword() , := getHostPortWithDefaults() .Addr = net.JoinHostPort(, ) := strings.FieldsFunc(.Path, func( rune) bool {return == '/' })switchlen() {case0: .DB = 0case1:varerrorif .DB, = strconv.Atoi([0]); != nil {returnnil, fmt.Errorf("redis: invalid database number: %q", [0]) }default:returnnil, fmt.Errorf("redis: invalid URL path: %s", .Path) }if .Scheme == "rediss" { .TLSConfig = &tls.Config{ServerName: ,MinVersion: tls.VersionTLS12, } }returnsetupConnParams(, )}// getHostPortWithDefaults is a helper function that splits the url into// a host and a port. If the host is missing, it defaults to localhost// and if the port is missing, it defaults to 6379.func getHostPortWithDefaults( *url.URL) (string, string) { , , := net.SplitHostPort(.Host)if != nil { = .Host }if == "" { = "localhost" }if == "" { = "6379" }return , }func setupUnixConn( *url.URL) (*Options, error) { := &Options{Network: "unix", }ifstrings.TrimSpace(.Path) == "" { // path is required with unix connectionreturnnil, errors.New("redis: empty unix socket path") } .Addr = .Path .Username, .Password = getUserPassword()returnsetupConnParams(, )}type queryOptions struct { q url.Values err error}func ( *queryOptions) ( string) bool {returnlen(.q[]) > 0}func ( *queryOptions) ( string) string { := .q[]iflen() == 0 {return"" }delete(.q, ) // enable detection of unknown parametersreturn [len()-1]}func ( *queryOptions) ( string) []string { := .q[]delete(.q, )return}func ( *queryOptions) ( string) int { := .string()if == "" {return0 } , := strconv.Atoi()if == nil {return }if .err == nil { .err = fmt.Errorf("redis: invalid %s number: %s", , ) }return0}func ( *queryOptions) ( string) time.Duration { := .string()if == "" {return0 }// try plain number firstif , := strconv.Atoi(); == nil {if <= 0 {// disable timeoutsreturn -1 }returntime.Duration() * time.Second } , := time.ParseDuration()if == nil {return }if .err == nil { .err = fmt.Errorf("redis: invalid %s duration: %w", , ) }return0}func ( *queryOptions) ( string) bool {switch := .string(); {case"true", "1":returntruecase"false", "0", "":returnfalsedefault:if .err == nil { .err = fmt.Errorf("redis: invalid %s boolean: expected true/false/1/0 or an empty string, got %q", , ) }returnfalse }}func ( *queryOptions) () []string {iflen(.q) == 0 {returnnil } := make([]string, 0, len(.q))for := range .q { = append(, ) }sort.Strings()return}// setupConnParams converts query parameters in u to option value in o.func setupConnParams( *url.URL, *Options) (*Options, error) { := queryOptions{q: .Query()}// compat: a future major release may use q.int("db")if := .string("db"); != "" { , := strconv.Atoi()if != nil {returnnil, fmt.Errorf("redis: invalid database number: %w", ) } .DB = } .ClientName = .string("client_name") .MaxRetries = .int("max_retries") .MinRetryBackoff = .duration("min_retry_backoff") .MaxRetryBackoff = .duration("max_retry_backoff") .DialTimeout = .duration("dial_timeout") .ReadTimeout = .duration("read_timeout") .WriteTimeout = .duration("write_timeout") .PoolFIFO = .bool("pool_fifo") .PoolSize = .int("pool_size") .PoolTimeout = .duration("pool_timeout") .MinIdleConns = .int("min_idle_conns") .MaxIdleConns = .int("max_idle_conns")if .has("conn_max_idle_time") { .ConnMaxIdleTime = .duration("conn_max_idle_time") } else { .ConnMaxIdleTime = .duration("idle_timeout") }if .has("conn_max_lifetime") { .ConnMaxLifetime = .duration("conn_max_lifetime") } else { .ConnMaxLifetime = .duration("max_conn_age") }if .err != nil {returnnil, .err }// any parameters left?if := .remaining(); len() > 0 {returnnil, fmt.Errorf("redis: unexpected option: %s", strings.Join(, ", ")) }return , nil}func getUserPassword( *url.URL) (string, string) {var , stringif .User != nil { = .User.Username()if , := .User.Password(); { = } }return , }func newConnPool( *Options,func( context.Context, , string) (net.Conn, error),) *pool.ConnPool {returnpool.NewConnPool(&pool.Options{Dialer: func( context.Context) (net.Conn, error) {return (, .Network, .Addr) },PoolFIFO: .PoolFIFO,PoolSize: .PoolSize,PoolTimeout: .PoolTimeout,MinIdleConns: .MinIdleConns,MaxIdleConns: .MaxIdleConns,ConnMaxIdleTime: .ConnMaxIdleTime,ConnMaxLifetime: .ConnMaxLifetime, })}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.