一般而言,我们用 etcd 的 watcher 时,都是像下面这样用,永不休止。
1 |
|
但是有时候,我们希望达到某种条件时,终止 watcher。翻看 etcd/client 的文档,没找到类似 watcher.Close() 、 watcher.Stop() 之类的方法,后来才知道,原来这些控制权都交给了 context了。
context 原先只是一个备胎库 golang.org/x/net/context
, go 1.7 以后被纳入标准库 context
了,转正成功。 一句话概括,它是一个管理协程树生命周期的解决方案。就是父协程能通过 context 来控制其子协程什么时候退出。
如果读者想了解更多关于 context 资料,可阅读下面的链接内容:
- https://godoc.org/golang.org/x/net/context
- https://blog.golang.org/context
- http://blog.golang.org/pipelines
- http://lanlingzi.cn/post/technical/2016/0802_go_context/ (中文)
回到正题 “如何结束 etcd watcher” 上来。
由于 context.Background return a non-nil, empty Context. It is never canceled, has no values, and has no deadline.
, 所以不指望他能干啥了。但是 WithCancel 可以帮助我们。
1 | func WithCancel(parent Context) (ctx Context, cancel CancelFunc) |
WithCancel 返回的 cancel 函数,能让我们结束其 child context, 也就是包含了其 child context 的 goroutine 也会退出,当然这是由开发者自己正确实现才起作用的。
子协程实现的套路是这样的:
1 | select { |
很显然(看代码得知,😝),etcd client 是实现了这种方法。
但是,什么时候去调用 cancel() 方法呢? 这当然取决于自己的业务需求。
举个例子:
1 | func watch(kapi *client.KeysAPI, node string, stopChan chan struct{}) error { |
我也看过别的写法,总而言之都是取决于业务的需要。
题外话,go 1.7 的 net/http 已经大力推广 context 包了,推荐使用。