Redigo: 支持上下文感知连接

创建于 2018-05-25  ·  18评论  ·  资料来源: gomodule/redigo

与实现Do/Receive方法和额外time.Duration参数的ConnWithTimeout接口类似,我认为添加一个ConnWithContext接口会很有用一个额外的context.Context参数。 许多其他 Golang 库开始采用context.Context用法来取消,因为它被放入标准库中。 此外,此提议的更改不应对此包的公共 API 进行任何破坏性更改。 如果我从维护这个包的人那里得到绿灯,我很高兴实现这个功能 - 我只是想首先根据https://github.com/gomodule/redigo/blob/master/.github/CONTRIBUTING开始讨论

最有用的评论

我试图解决的问题是取消,超时只是一个特例。 context.Context接口公开了一个Done() <-chan struct{}方法来检查取消发生的时间和Err() error来检查相关错误,但该错误值不一定来自超时(例如,它可以通过context.WithCancel从操作系统捕获 SIGINT 并优雅地停止 goroutine 之后)。

所有18条评论

我计划使用它来处理客户端访问网络时的超时(在这种情况下,使用Do )。

我不知道发生超时错误时连接已关闭,但经过一些测试后,我现在在本地看到了这一点。 这是这个包中的预期行为吗? 恕我直言,每次操作超时时都必须创建一个新连接似乎有点多。 是的,您上面发布的代码段基本上就是我对代码添加的看法。

我同意 - 一般来说,Redis 客户端似乎不应该在取消/超时时默认关闭,而是将其留给调用代码来处理它认为合适的情况。 为了一致性,更新DoWithTimeout代码也可能是值得的,假设上下文代码进入。

编辑:这种变化的向后兼容性让我有点担心,特别是考虑到这个包有很多用户。

https://github.com/juhanoi/radix客户端实际上有一个strict参数,它传递给它的内部网络读取函数,该函数作为一个开关来确定是否应该将超时视为严重错误(参见 https://github .com/mediocregopher/radix.v2/blob/master/redis/client.go#L178)。 它在发生超时和strict == true时关闭连接,但它似乎在客户端的公共 API 上默认被禁用。 它还公开了一个公共IsTimeout函数来检查错误值(参见 https://github.com/mediocregopher/radix.v2/blob/master/redis/resp.go#L826)。

然而, radix包中的客户端不是线程安全的,因此只能在单个 goroutine 中使用。 您必须使用pool子包从有限的池中获取新的客户端实例来处理其他事情。 我认为这可能值得一提,以防它与您上面提到的连接同步主题相关,因为我知道这个包通过由互斥锁保护的单个 TCP 连接处理客户端线程安全。

您要解决的问题是什么?

你说你想要处理超时。 拨号连接时可以指定读取和写入超时,并且应用程序可以在每次调用的基础上覆盖读取超时。 解释为什么这不能满足您的需求。

添加上下文参数可能是解决问题的一部分,但让我们从对问题的讨论开始。

我试图解决的问题是取消,超时只是一个特例。 context.Context接口公开了一个Done() <-chan struct{}方法来检查取消发生的时间和Err() error来检查相关错误,但该错误值不一定来自超时(例如,它可以通过context.WithCancel从操作系统捕获 SIGINT 并优雅地停止 goroutine 之后)。

描述您尝试在应用程序中解决的具体问题。 我知道上下文的作用。 这里无需重复描述。

我正在使用多阶段管道,其中第 3 阶段涉及使用 Redis 作为缓存来检测重复值,但依赖于仍在运行的其他阶段才能继续。 如果另一个阶段在运行时的某个时间点失败,我希望能够在所有其他阶段启动早期取消,而无需等待每个长时间运行的任务结束。

Redis 不提供取消命令的方法。 哪个 Redis 命令花费的时间太长以至于令人担忧? 您是否正在使用阻塞命令之一?

没有阻塞命令,只有像GETSETEX这样的基本东西。 对我来说,这更像是一个统一问题——我的应用程序的所有其他部分都需要context.Context来取消,我认为这将是对这个包的 API 的一个很好的补充。

回复时误点击了关闭按钮,抱歉。

正如我们之前所讨论的,我还设置了超时,但目前我已经可以通过使用DoWithTimeout函数来单独设置。 相反,我为context.Context提出了进一步的论点,因为它可以在我的程序布局中更加概括,因为并非所有取消都是超时。

是否有需要取消的 Redis 命令?

@redmondnoodle我不推荐你提出的DoWithContext函数。 如果上下文超时小于执行命令所需的时间,则连接超时并且不再可用。 如果上下文超时远大于执行命令的预期时间,则需要更长的时间才能检测到服务器关闭或网络连接中断。

我正在关闭这个问题,因为对于新功能没有强有力的论据。 尽管应用程序阶段可能会长时间运行,但对 GET 和 SETEX 的单独调用不会。 应用程序可以在不涉及此包的情况下终止一个阶段。 拨号超时选项处理 GET 和 SETEX 命令的超时。

当没有要添加的功能时,无需更改 API。

@garyburd我有这个用例:通过“BRPOP key 0”(0 - 所以没有超时)产生一个 goroutine 等待某事发生。 现在,正如@smotes上面提到的,我需要在收到 SIGINT 后正常关闭。 这样做的通常方法是使用上下文。 如果没有适当的上下文,如何正确地向执行 BRPOP 的 goroutine 发出信号以停止它正在执行的操作?

@pennersr见#351。

@garyburd虽然#351 中的 DoWithCancel() 可用于实现我的用例,但这种方法确实导致我的每个 Do() 命令都有额外的开销/延迟,以便额外的 Redis 命令获取客户端 ID——如果有取消 Redis 连接中任何正在进行的命令的“本机”支持,则不需要的东西。

FWIW,我现在通过在触发取消时相当直率地发出 conn.Close() 来实现我的用例。 这会中止正在进行的 Do("BRPOP"),尽管有错误:

2018/06/26 21:49:48 Error BRPOP:read tcp 172.19.0.6:58856->172.19.0.3:6379: use of closed network connection

通过在低级连接中缓存 CLIENT ID 命令的结果,可以消除到服务器的额外往返。 我更新了 351 来提及这一点。

本机支持无法消除与 goroutine 协调以等待上下文完成通道的开销。 我建议将 DoWithCancel() 作为单独的帮助程序,以便应用程序仅在需要时才产生这种额外的开销。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

Serhioromano picture Serhioromano  ·  7评论

V2Vz picture V2Vz  ·  4评论

lukasmalkmus picture lukasmalkmus  ·  18评论

garyburd picture garyburd  ·  23评论

lovegnep picture lovegnep  ·  18评论