在导出的 golang 变量中声明的 运行 docker 容器的最新列表
Uptodate list of running docker containers stated in an exported golang variable
我正在尝试使用 Docker 的 Golang SDK,以便在本地 Docker 实例上使用当前 运行 容器维护切片变量。这个切片是从一个包中导出的,我想用它来提供网页。
我不太习惯 goroutines 和通道,这就是为什么我想知道我是否找到了解决问题的好方法。
我有一个docker
包如下。
https://play.golang.org/p/eMmqkMezXZn
它有一个 Running
变量,其中包含 运行 个容器的当前状态。
var Running []types.Container
我使用 reload
函数在 Running
变量中加载 运行 个容器。
// Reload the list of running containers
func reload() error {
...
Running, err = cli.ContainerList(context.Background(), types.ContainerListOptions{
All: false,
})
...
}
然后我从 init
函数启动一个 goroutine 来监听 Docker 事件并相应地触发 reload
函数。
func init() {
...
// Listen for docker events
go listen()
...
}
// Listen for docker events
func listen() {
filter := filters.NewArgs()
filter.Add("type", "container")
filter.Add("event", "start")
filter.Add("event", "die")
msg, errChan := cli.Events(context.Background(), types.EventsOptions{
Filters: filter,
})
for {
select {
case err := <-errChan:
panic(err)
case <-msg:
fmt.Println("reloading")
reload()
}
}
}
我的问题是,从 goroutine 内部更新变量是否合适(就同步而言)?也许有一种更简洁的方法来实现我想要构建的东西?
更新
我真正关心的不是缓存。更多的是对Docker SDK隐藏监听和更新过程的"complexity"。我想提供类似索引的东西,以便最终用户轻松循环并显示当前 运行 个容器。
我知道线程程序中的数据争用问题,但我没有意识到我实际上处于并发上下文中(我以前从未在 Go 中编写过并发程序)。
我实际上需要重新考虑解决方案,使其更加地道。据我所知,我在这里有两个选择:用互斥锁保护变量或重新考虑设计以集成通道。
对我来说最重要的是隐藏或封装使用的同步方法,这样包用户就不必关心共享状态是如何保护的。
您有什么建议吗?
非常感谢您的帮助,
洛里克
不,在两个 goroutine 之间共享 Running
变量不是惯用的 Go。您可以通过在运行 main
函数的例程和以 go
启动的 listen
函数之间共享它来做到这一点——这会产生另一个 goroutine。
为什么,是因为它打破了
Do not communicate by sharing memory; instead, share memory by
communicating. ¹
因此 API 的设计需要更改才能符合惯用语;您需要删除 Running
变量并将其替换为什么?这取决于您要实现的目标。如果你试图缓存 cli.ContainerList
因为你需要经常调用它,而且它可能很昂贵,你应该实现一个在每个 cli.Events
.
上失效的缓存
你的动机是什么?
我正在尝试使用 Docker 的 Golang SDK,以便在本地 Docker 实例上使用当前 运行 容器维护切片变量。这个切片是从一个包中导出的,我想用它来提供网页。
我不太习惯 goroutines 和通道,这就是为什么我想知道我是否找到了解决问题的好方法。
我有一个docker
包如下。
https://play.golang.org/p/eMmqkMezXZn
它有一个 Running
变量,其中包含 运行 个容器的当前状态。
var Running []types.Container
我使用 reload
函数在 Running
变量中加载 运行 个容器。
// Reload the list of running containers
func reload() error {
...
Running, err = cli.ContainerList(context.Background(), types.ContainerListOptions{
All: false,
})
...
}
然后我从 init
函数启动一个 goroutine 来监听 Docker 事件并相应地触发 reload
函数。
func init() {
...
// Listen for docker events
go listen()
...
}
// Listen for docker events
func listen() {
filter := filters.NewArgs()
filter.Add("type", "container")
filter.Add("event", "start")
filter.Add("event", "die")
msg, errChan := cli.Events(context.Background(), types.EventsOptions{
Filters: filter,
})
for {
select {
case err := <-errChan:
panic(err)
case <-msg:
fmt.Println("reloading")
reload()
}
}
}
我的问题是,从 goroutine 内部更新变量是否合适(就同步而言)?也许有一种更简洁的方法来实现我想要构建的东西?
更新
我真正关心的不是缓存。更多的是对Docker SDK隐藏监听和更新过程的"complexity"。我想提供类似索引的东西,以便最终用户轻松循环并显示当前 运行 个容器。
我知道线程程序中的数据争用问题,但我没有意识到我实际上处于并发上下文中(我以前从未在 Go 中编写过并发程序)。
我实际上需要重新考虑解决方案,使其更加地道。据我所知,我在这里有两个选择:用互斥锁保护变量或重新考虑设计以集成通道。
对我来说最重要的是隐藏或封装使用的同步方法,这样包用户就不必关心共享状态是如何保护的。
您有什么建议吗?
非常感谢您的帮助, 洛里克
不,在两个 goroutine 之间共享 Running
变量不是惯用的 Go。您可以通过在运行 main
函数的例程和以 go
启动的 listen
函数之间共享它来做到这一点——这会产生另一个 goroutine。
为什么,是因为它打破了
Do not communicate by sharing memory; instead, share memory by communicating. ¹
因此 API 的设计需要更改才能符合惯用语;您需要删除 Running
变量并将其替换为什么?这取决于您要实现的目标。如果你试图缓存 cli.ContainerList
因为你需要经常调用它,而且它可能很昂贵,你应该实现一个在每个 cli.Events
.
你的动机是什么?