123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- // Package radix implements all functionality needed to work with redis and all
- // things related to it, including redis cluster, pubsub, sentinel, scanning,
- // lua scripting, and more.
- //
- // Creating a client
- //
- // For a single node redis instance use NewPool to create a connection pool. The
- // connection pool is thread-safe and will automatically create, reuse, and
- // recreate connections as needed:
- //
- // pool, err := radix.NewPool("tcp", "127.0.0.1:6379", 10)
- // if err != nil {
- // // handle error
- // }
- //
- // If you're using sentinel or cluster you should use NewSentinel or NewCluster
- // (respectively) to create your client instead.
- //
- // Commands
- //
- // Any redis command can be performed by passing a Cmd into a Client's Do
- // method. Each Cmd should only be used once. The return from the Cmd can be
- // captured into any appopriate go primitive type, or a slice, map, or struct,
- // if the command returns an array.
- //
- // err := client.Do(radix.Cmd(nil, "SET", "foo", "someval"))
- //
- // var fooVal string
- // err := client.Do(radix.Cmd(&fooVal, "GET", "foo"))
- //
- // var fooValB []byte
- // err := client.Do(radix.Cmd(&fooValB, "GET", "foo"))
- //
- // var barI int
- // err := client.Do(radix.Cmd(&barI, "INCR", "bar"))
- //
- // var bazEls []string
- // err := client.Do(radix.Cmd(&bazEls, "LRANGE", "baz", "0", "-1"))
- //
- // var buzMap map[string]string
- // err := client.Do(radix.Cmd(&buzMap, "HGETALL", "buz"))
- //
- // FlatCmd can also be used if you wish to use non-string arguments like
- // integers, slices, maps, or structs, and have them automatically be flattened
- // into a single string slice.
- //
- // Struct Scanning
- //
- // Cmd and FlatCmd can unmarshal results into a struct. The results must be a
- // key/value array, such as that returned by HGETALL. Exported field names will
- // be used as keys, unless the fields have the "redis" tag:
- //
- // type MyType struct {
- // Foo string // Will be populated with the value for key "Foo"
- // Bar string `redis:"BAR"` // Will be populated with the value for key "BAR"
- // Baz string `redis:"-"` // Will not be populated
- // }
- //
- // Embedded structs will inline that struct's fields into the parent's:
- //
- // type MyOtherType struct {
- // // adds fields "Foo" and "BAR" (from above example) to MyOtherType
- // MyType
- // Biz int
- // }
- //
- // The same rules for field naming apply when a struct is passed into FlatCmd as
- // an argument.
- //
- // Actions
- //
- // Cmd and FlatCmd both implement the Action interface. Other Actions include
- // Pipeline, WithConn, and EvalScript.Cmd. Any of these may be passed into any
- // Client's Do method.
- //
- // var fooVal string
- // p := radix.Pipeline(
- // radix.FlatCmd(nil, "SET", "foo", 1),
- // radix.Cmd(&fooVal, "GET", "foo"),
- // )
- // if err := client.Do(p); err != nil {
- // panic(err)
- // }
- // fmt.Printf("fooVal: %q\n", fooVal)
- //
- // Transactions
- //
- // There are two ways to perform transactions in redis. The first is with the
- // MULTI/EXEC commands, which can be done using the WithConn Action (see its
- // example). The second is using EVAL with lua scripting, which can be done
- // using the EvalScript Action (again, see its example).
- //
- // EVAL with lua scripting is recommended in almost all cases. It only requires
- // a single round-trip, it's infinitely more flexible than MULTI/EXEC, it's
- // simpler to code, and for complex transactions, which would otherwise need a
- // WATCH statement with MULTI/EXEC, it's significantly faster.
- //
- // AUTH and other settings via ConnFunc and ClientFunc
- //
- // All the client creation functions (e.g. NewPool) take in either a ConnFunc or
- // a ClientFunc via their options. These can be used in order to set up timeouts
- // on connections, perform authentication commands, or even implement custom
- // pools.
- //
- // // this is a ConnFunc which will set up a connection which is authenticated
- // // and has a 1 minute timeout on all operations
- // customConnFunc := func(network, addr string) (radix.Conn, error) {
- // return radix.Dial(network, addr,
- // radix.DialTimeout(1 * time.Minute),
- // radix.DialAuthPass("mySuperSecretPassword"),
- // )
- // }
- //
- // // this pool will use our ConnFunc for all connections it creates
- // pool, err := radix.NewPool("tcp", redisAddr, 10, PoolConnFunc(customConnFunc))
- //
- // // this cluster will use the ClientFunc to create a pool to each node in the
- // // cluster. The pools also use our customConnFunc, but have more connections
- // poolFunc := func(network, addr string) (radix.Client, error) {
- // return radix.NewPool(network, addr, 100, PoolConnFunc(customConnFunc))
- // }
- // cluster, err := radix.NewCluster([]string{redisAddr1, redisAddr2}, ClusterPoolFunc(poolFunc))
- //
- // Custom implementations
- //
- // All interfaces in this package were designed such that they could have custom
- // implementations. There is no dependency within radix that demands any
- // interface be implemented by a particular underlying type, so feel free to
- // create your own Pools or Conns or Actions or whatever makes your life easier.
- //
- // Errors
- //
- // Errors returned from redis can be explicitly checked for using the the
- // resp2.Error type. Note that the errors.As function, introduced in go 1.13,
- // should be used.
- //
- // var redisErr resp2.Error
- // err := client.Do(radix.Cmd(nil, "AUTH", "wrong password"))
- // if errors.As(err, &redisErr) {
- // log.Printf("redis error returned: %s", redisErr.E)
- // }
- //
- // Use the golang.org/x/xerrors package if you're using an older version of go.
- //
- // Implicit pipelining
- //
- // Implicit pipelining is an optimization implemented and enabled in the default
- // Pool implementation (and therefore also used by Cluster and Sentinel) which
- // involves delaying concurrent Cmds and FlatCmds a small amount of time and
- // sending them to redis in a single batch, similar to manually using a Pipeline.
- // By doing this radix significantly reduces the I/O and CPU overhead for
- // concurrent requests.
- //
- // Note that only commands which do not block are eligible for implicit pipelining.
- //
- // See the documentation on Pool for more information about the current
- // implementation of implicit pipelining and for how to configure or disable
- // the feature.
- //
- // For a performance comparisons between Clients with and without implicit
- // pipelining see the benchmark results in the README.md.
- //
- package radix
- import (
- "errors"
- )
- var errClientClosed = errors.New("client is closed")
- // Client describes an entity which can carry out Actions, e.g. a connection
- // pool for a single redis instance or the cluster client.
- //
- // Implementations of Client are expected to be thread-safe, except in cases
- // like Conn where they specify otherwise.
- type Client interface {
- // Do performs an Action, returning any error.
- Do(Action) error
- // Once Close() is called all future method calls on the Client will return
- // an error
- Close() error
- }
- // ClientFunc is a function which can be used to create a Client for a single
- // redis instance on the given network/address.
- type ClientFunc func(network, addr string) (Client, error)
- // DefaultClientFunc is a ClientFunc which will return a Client for a redis
- // instance using sane defaults.
- var DefaultClientFunc = func(network, addr string) (Client, error) {
- return NewPool(network, addr, 4)
- }
|