如何在 golang 中向 io.ReadCloser 显式发送 EOF?

问题描述

希望您能健康收到此消息。

所以在我正在研究的 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 是任何实现 ReadClose 方法(具有 io.Readerio.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 条件。