Go Redis Hook 钩子

go-redis 允许配置 Hook,在执行命令前后可以做一些工作,也可以改变命令的参数、执行结果等。

Hook 采用 FIFO 先进先出的模式,即最先添加的 hook,最先被执行,以下是 hook 示例:

// 添加的钩子,必须实现Hook接口
// DialHook: 当创建网络连接时调用的hook
// ProcessHook: 执行命令时调用的hook
// ProcessPipelineHook: 执行管道命令时调用的hook
type Hook interface {
	DialHook(next DialHook) DialHook
	ProcessHook(next ProcessHook) ProcessHook
	ProcessPipelineHook(next ProcessPipelineHook) ProcessPipelineHook
}

// -------- hook1

type Hook1 struct{}

func (Hook1) DialHook(next redis.DialHook) redis.DialHook {
	return func(ctx context.Context, network, addr string) (net.Conn, error) {
        return next(ctx, network, addr)
    }
}
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
	}
}
func (Hook1) ProcessPipelineHook(next redis.ProcessPipelineHook) redis.ProcessPipelineHook {
	return func(ctx context.Context, cmds []Cmder) error {
		return next(ctx, cmds)
    }
}

// -------- hook2

type Hook2 struct{}

func (Hook2) DialHook(next redis.DialHook) redis.DialHook {
    return func(ctx context.Context, network, addr string) (net.Conn, error) {
        next(ctx, network, addr)
    }
}
func (Hook2) ProcessHook(next redis.ProcessHook) redis.ProcessHook {
    return func(ctx context.Context, cmd Cmder) error {
        print("hook-2 start")
        next(ctx, cmd)
        print("hook-2 end")
        return nil
    }
}
func (Hook2) ProcessPipelineHook(next redis.ProcessPipelineHook) redis.ProcessPipelineHook {
    return func(ctx context.Context, cmds []Cmder) error {
        return next(ctx, cmds)
    }
}

// 把两个hook添加到client
client.AddHook(Hook1{}, Hook2{})
client.Get(ctx, "key")

如上所述,对 client 添加了 hook1 和 hook2,2 个钩子都对 ProcessHook 执行了 print 函数,当执行命令时,调用的顺序如下:

hook-1 start -> hook-2 start -> exec redis cmd -> hook-2 end -> hook-1 end

在这里请注意:next(...) 操作很重要,是调用下一个 hook,在 hook 调用链中,最后一个 hook 是 redis 执行命令操作。

在示例中,如果 hook1 ProcessHook 中如果不执行 next,则不会执行 hook2 也不会执行 redis 命令。

Hook 支持三个挂钩点,分别是:

  1. DialHook: 当创建网络连接时调用的 hook
  2. ProcessHook: 执行命令时调用的 hook
  3. ProcessPipelineHook: 执行管道命令时调用的 hook

在不同的挂钩点你可以做不同的事,可以统计命令个数计算 top key,也可以统计命令执行时间,也可以做一些其它你想要做的操作。

在 hook 函数中,可以手动为 cmd 命令设置错误和返回值,调用方将收到你设置的错误和值,这对 mock 测试很有用,在 go-redis 的 redismock在新窗口打开 中就利用了 hook 截断要执行的命令,对命令写入了测试中期望的返回值或错误。

你也可以返回错误来终止函数,你的错误最终会传递到调用命令的地方。