forked from mirror/redis
Merge pull request #2344 from monkey92t/hook
docs: add a description of the hook
This commit is contained in:
commit
09fcdbe6e4
|
@ -136,10 +136,13 @@ func (opt *ClusterOptions) init() {
|
||||||
|
|
||||||
// ParseClusterURL parses a URL into ClusterOptions that can be used to connect to Redis.
|
// ParseClusterURL parses a URL into ClusterOptions that can be used to connect to Redis.
|
||||||
// The URL must be in the form:
|
// The URL must be in the form:
|
||||||
|
//
|
||||||
// redis://<user>:<password>@<host>:<port>
|
// redis://<user>:<password>@<host>:<port>
|
||||||
// or
|
// or
|
||||||
// rediss://<user>:<password>@<host>:<port>
|
// rediss://<user>:<password>@<host>:<port>
|
||||||
|
//
|
||||||
// To add additional addresses, specify the query parameter, "addr" one or more times. e.g:
|
// To add additional addresses, specify the query parameter, "addr" one or more times. e.g:
|
||||||
|
//
|
||||||
// redis://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
|
// redis://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
|
||||||
// or
|
// or
|
||||||
// rediss://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
|
// rediss://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
|
||||||
|
@ -156,7 +159,9 @@ func (opt *ClusterOptions) init() {
|
||||||
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these
|
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these
|
||||||
// names will be treated as unknown parameters
|
// names will be treated as unknown parameters
|
||||||
// - unknown parameter names will result in an error
|
// - unknown parameter names will result in an error
|
||||||
|
//
|
||||||
// Example:
|
// Example:
|
||||||
|
//
|
||||||
// redis://user:password@localhost:6789?dial_timeout=3&read_timeout=6s&addr=localhost:6790&addr=localhost:6791
|
// redis://user:password@localhost:6789?dial_timeout=3&read_timeout=6s&addr=localhost:6790&addr=localhost:6791
|
||||||
// is equivalent to:
|
// is equivalent to:
|
||||||
// &ClusterOptions{
|
// &ClusterOptions{
|
||||||
|
|
17
command.go
17
command.go
|
@ -1110,15 +1110,16 @@ func (cmd *KeyValueSliceCmd) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Many commands will respond to two formats:
|
// Many commands will respond to two formats:
|
||||||
// 1) 1) "one"
|
// 1. 1) "one"
|
||||||
// 2) (double) 1
|
// 2. (double) 1
|
||||||
// 2) 1) "two"
|
// 2. 1) "two"
|
||||||
// 2) (double) 2
|
// 2. (double) 2
|
||||||
|
//
|
||||||
// OR:
|
// OR:
|
||||||
// 1) "two"
|
// 1. "two"
|
||||||
// 2) (double) 2
|
// 2. (double) 2
|
||||||
// 3) "one"
|
// 3. "one"
|
||||||
// 4) (double) 1
|
// 4. (double) 1
|
||||||
func (cmd *KeyValueSliceCmd) readReply(rd *proto.Reader) error { // nolint:dupl
|
func (cmd *KeyValueSliceCmd) readReply(rd *proto.Reader) error { // nolint:dupl
|
||||||
n, err := rd.ReadArrayLen()
|
n, err := rd.ReadArrayLen()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -32,7 +32,9 @@ type Once struct {
|
||||||
|
|
||||||
// Do calls the function f if and only if Do has not been invoked
|
// Do calls the function f if and only if Do has not been invoked
|
||||||
// without error for this instance of Once. In other words, given
|
// without error for this instance of Once. In other words, given
|
||||||
|
//
|
||||||
// var once Once
|
// var once Once
|
||||||
|
//
|
||||||
// if once.Do(f) is called multiple times, only the first call will
|
// if once.Do(f) is called multiple times, only the first call will
|
||||||
// invoke f, even if f has a different value in each invocation unless
|
// invoke f, even if f has a different value in each invocation unless
|
||||||
// f returns an error. A new instance of Once is required for each
|
// f returns an error. A new instance of Once is required for each
|
||||||
|
@ -41,6 +43,7 @@ type Once struct {
|
||||||
// Do is intended for initialization that must be run exactly once. Since f
|
// Do is intended for initialization that must be run exactly once. Since f
|
||||||
// is niladic, it may be necessary to use a function literal to capture the
|
// is niladic, it may be necessary to use a function literal to capture the
|
||||||
// arguments to a function to be invoked by Do:
|
// arguments to a function to be invoked by Do:
|
||||||
|
//
|
||||||
// err := config.once.Do(func() error { return config.init(filename) })
|
// err := config.once.Do(func() error { return config.init(filename) })
|
||||||
func (o *Once) Do(f func() error) error {
|
func (o *Once) Do(f func() error) error {
|
||||||
if atomic.LoadUint32(&o.done) == 1 {
|
if atomic.LoadUint32(&o.done) == 1 {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Scan parses bytes `b` to `v` with appropriate type.
|
// Scan parses bytes `b` to `v` with appropriate type.
|
||||||
|
//
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func Scan(b []byte, v interface{}) error {
|
func Scan(b []byte, v interface{}) error {
|
||||||
switch v := v.(type) {
|
switch v := v.(type) {
|
||||||
|
|
|
@ -223,9 +223,13 @@ func NewDialer(opt *Options) func(context.Context, string, string) (net.Conn, er
|
||||||
// Scheme is required.
|
// Scheme is required.
|
||||||
// There are two connection types: by tcp socket and by unix socket.
|
// There are two connection types: by tcp socket and by unix socket.
|
||||||
// Tcp connection:
|
// Tcp connection:
|
||||||
|
//
|
||||||
// redis://<user>:<password>@<host>:<port>/<db_number>
|
// redis://<user>:<password>@<host>:<port>/<db_number>
|
||||||
|
//
|
||||||
// Unix connection:
|
// Unix connection:
|
||||||
|
//
|
||||||
// unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
|
// unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
|
||||||
|
//
|
||||||
// Most Option fields can be set using query parameters, with the following restrictions:
|
// 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
|
// - field names are mapped using snake-case conversion: to set MaxRetries, use max_retries
|
||||||
// - only scalar type fields are supported (bool, int, time.Duration)
|
// - only scalar type fields are supported (bool, int, time.Duration)
|
||||||
|
@ -238,7 +242,9 @@ func NewDialer(opt *Options) func(context.Context, string, string) (net.Conn, er
|
||||||
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these
|
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these
|
||||||
// names will be treated as unknown parameters
|
// names will be treated as unknown parameters
|
||||||
// - unknown parameter names will result in an error
|
// - unknown parameter names will result in an error
|
||||||
|
//
|
||||||
// Examples:
|
// Examples:
|
||||||
|
//
|
||||||
// redis://user:password@localhost:6789/3?dial_timeout=3&db=1&read_timeout=6s&max_retries=2
|
// redis://user:password@localhost:6789/3?dial_timeout=3&db=1&read_timeout=6s&max_retries=2
|
||||||
// is equivalent to:
|
// is equivalent to:
|
||||||
// &Options{
|
// &Options{
|
||||||
|
|
45
redis.go
45
redis.go
|
@ -29,9 +29,9 @@ func SetLogger(logger internal.Logging) {
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
type Hook interface {
|
type Hook interface {
|
||||||
DialHook(hook DialHook) DialHook
|
DialHook(next DialHook) DialHook
|
||||||
ProcessHook(hook ProcessHook) ProcessHook
|
ProcessHook(next ProcessHook) ProcessHook
|
||||||
ProcessPipelineHook(hook ProcessPipelineHook) ProcessPipelineHook
|
ProcessPipelineHook(next ProcessPipelineHook) ProcessPipelineHook
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -48,6 +48,43 @@ type hooks struct {
|
||||||
processTxPipelineHook ProcessPipelineHook
|
processTxPipelineHook ProcessPipelineHook
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddHook is to add a hook to the queue.
|
||||||
|
// Hook is a function executed during network connection, command execution, and pipeline,
|
||||||
|
// it is a first-in-last-out stack queue (FILO).
|
||||||
|
// The first to be added to the queue is the execution function of the redis command (the last to be executed).
|
||||||
|
// You need to execute the next hook in each hook, unless you want to terminate the execution of the command.
|
||||||
|
// For example, you added hook-1, hook-2:
|
||||||
|
//
|
||||||
|
// client.AddHook(hook-1, hook-2)
|
||||||
|
//
|
||||||
|
// hook-1:
|
||||||
|
//
|
||||||
|
// func (Hook1) ProcessHook(next redis.ProcessHook) redis.ProcessHook {
|
||||||
|
// return func(ctx context.Context, cmd Cmder) error {
|
||||||
|
// print("hook-1 start")
|
||||||
|
// next(ctx, cmd)
|
||||||
|
// print("hook-1 end")
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// hook-2:
|
||||||
|
//
|
||||||
|
// func (Hook2) ProcessHook(next redis.ProcessHook) redis.ProcessHook {
|
||||||
|
// return func(ctx context.Context, cmd redis.Cmder) error {
|
||||||
|
// print("hook-2 start")
|
||||||
|
// next(ctx, cmd)
|
||||||
|
// print("hook-2 end")
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The execution sequence is:
|
||||||
|
//
|
||||||
|
// hook-2 start -> hook-1 start -> exec redis cmd -> hook-1 end -> hook-2 end
|
||||||
|
//
|
||||||
|
// Please note: "next(ctx, cmd)" is very important, it will call the next hook,
|
||||||
|
// if "next(ctx, cmd)" is not executed in hook-1, the redis command will not be executed.
|
||||||
func (hs *hooks) AddHook(hook Hook) {
|
func (hs *hooks) AddHook(hook Hook) {
|
||||||
hs.slice = append(hs.slice, hook)
|
hs.slice = append(hs.slice, hook)
|
||||||
hs.dialHook = hook.DialHook(hs.dialHook)
|
hs.dialHook = hook.DialHook(hs.dialHook)
|
||||||
|
@ -575,7 +612,7 @@ func (c *Client) Conn() *Conn {
|
||||||
return newConn(c.opt, pool.NewStickyConnPool(c.connPool))
|
return newConn(c.opt, pool.NewStickyConnPool(c.connPool))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do creates a Cmd from the args and processes the cmd.
|
// Do create a Cmd from the args and processes the cmd.
|
||||||
func (c *Client) Do(ctx context.Context, args ...interface{}) *Cmd {
|
func (c *Client) Do(ctx context.Context, args ...interface{}) *Cmd {
|
||||||
cmd := NewCmd(ctx, args...)
|
cmd := NewCmd(ctx, args...)
|
||||||
_ = c.Process(ctx, cmd)
|
_ = c.Process(ctx, cmd)
|
||||||
|
|
Loading…
Reference in New Issue