问题描述
希望您能健康收到此消息。
所以在我正在研究的 golang 程序中存在这个错误,代码库中的这种阻塞方法 stdcopy.Stdcopy() 会导致一切挂起。出现这个问题是因为 Docker 不断地向 Stdcopy 阻塞方法发送信息,并且阻塞方法只有在向其发送 EOF 时才能停止运行。 Stdcopy 接受 ReadCloser 类型参数,这是阻塞方法从 Docker 读取信息的方式。 Goroutines 也在程序进入阻塞方法之前使用。换句话说,Stdcopy 阻塞方法在一个新线程中运行,而主线程与其并行运行。但是,阻塞方法现在阻塞了不能解决问题的新线程。
为了防止这个问题,我试图做的是在主线程中显式关闭这个 ReadCloser,然后向新线程发送一个 EOF,该线程持有 Stdcopy 阻塞方法,以通过超时错误强行停止它。这应该阻止整个程序挂起,因为阻塞方法是它挂起的原因。我通过存储我打开的每个 ReadCloser 的映射来做到这一点,然后在我们的程序停止时运行的特定方法中手动关闭它们。我不确定关闭 ReadCloser 接口是否会自动发送 EOF(我查看了 golang 文档,但找不到任何相关信息:https://golang.org/pkg/io/#ReadCloser)。
我决定明确地向 ReadCloser 发送一个 EOF 以防万一。但是,我该怎么做呢?我正在考虑使用我对通道的了解,并通过通道将 io.EOF 传递到运行阻塞方法的 go-routine 中。但是,io.ReadCloser 不是通道类型,所以我不能这样做。
关闭 io.ReadCloser 会自动发送 EOF 吗?如果没有,您将如何向 io.ReadCloser 发送 EOF 以便阻止方法停止运行?
如果我的解释中有任何不合理的地方,请告诉我,您希望我澄清。由于我对 golang 比较陌生,因此也可以在任何您认为我错误的地方纠正我。
非常感谢您抽出宝贵时间,请告诉我任何想法!
----- 编辑 -----
为了更好的理解,我写了一些代码:
//Closing all of the ReadClosers opened to prevent blocking
for k := range streamer.inputReadClosers {
(*k).Close()
//(*k) <- io.EOF //does not work since io.ReadCloser is not of channel type
}
因此对于某些上下文,streamer 只是一个结构,它包含一个属性调用 inputReadClosers,它是所有打开的 ReadClosers 的指针映射。然后我明确关闭 ReadCloser,然后目标是手动向该 ReadCloser 发送 EOF。
关于 ReadCloser 如何初始化的一些信息:
所以我有一个文件,其中包含一个方法,其中方法标题为 func (streamer *LogStreamer) StartStreamingFromDockerLogs(input io.ReadCloser) error
。当我检查其他文件中使用 StartStreamingFromDockerLogs(...)
的实例时,我发现传递了一个 readcloser 变量,并且 readcloser 变量以下列方式初始化:readCloser,err := dockerManager.GetContainerLogs(.....three parameters here…..)
。
解决方法
这实际上是一个评论,而不是一个答案,只是它太大而不能成为评论(并且需要一些格式)。
这里有一些基本的不匹配,使得这个问题无法解决:
-
首先,文件结尾,例如由
io.EOF
错误表示的,不是您可以通过任何类型的数据流发送的东西。这是一个适用于流的条件,在该条件下无法接收更多数据(至少目前 - 条件可能会在未来清除)。 -
其次,
io.ReadCloser
是任何实现Read
和Close
方法(具有io.Reader
和io.Closer
)的任何东西。你不能“发送”一个 EOF 到这个。当其Read
函数返回一个io.EOF
错误作为其两个返回值之一时,会发生 EOF。。如果某个类型 T 实现了io.ReadCloser
,并且您将 T 类型的值传递给某个函数 f,并且 f 调用它的Read
方法,并返回io.EOF
作为它的错误条件,然后这个 T 的读取器产生了一个 EOF 条件。
作为Penelope Stevens said in a comment,如果我们知道碰巧实现io.ReadCloser
的实际具体类型Tc,则 我们或许能够找到某种方法来说服 Tc 的读者返回一个 io.EOF
错误。这反过来又需要检查 Tc 的源代码或文档。例如:
type Foo struct {
// unexported fields
TemporaryEOF bool
}
...
func (Foo *f) Read(p []byte) (n int,err error) {
// if we're supposed to assert a temporary EOF now,do so
if f->temporaryEOF {
return 0,io.EOF
}
// rest of code here
}
// implement io.Closer() here too
这告诉我们,实际上实现了Foo
的{{1}}对象可以通过将其io.ReadCloser
数据字段设置为{ {1}}。
这不适用于实现 TemporaryEOF
的其他项目,因为它们没有 true
数据字段。这种在程序运行期间强制 EOF 一段时间的特殊方法是 io.ReadCloser
对象所特有的。 这正是我们无法解决的问题,因为我们没有正确的信息:使EOF条件发生的代码是Tc的TemporaryEOF
函数中的代码。 除非您向我们展示该代码——或允许我们找到该代码的东西——我们无法告诉您如何使其产生 EOF 条件。