【问题标题】:Reconnect TCP on EOF in Go在 Go 中重新连接 EOF 上的 TCP
【发布时间】:2014-04-30 18:31:28
【问题描述】:

我有以下几点:

    //In an init func
    if logStashHost != "" {
        lsconn, err = net.Dial("tcp", logStashHost)
    }
    ...
    ToLogStash(rec, lsconn)

然后是两个函数:

func ReadLogStash(conn net.Conn) {
    buffer := make([]byte, 256)
    for {
        _, err := conn.Read(buffer)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println(buffer)
        }
    }
}

func ToLogStash(r *logrow.Record, conn net.Conn) {
    b, err := json.Marshal(r)
    if err != nil {
        fmt.Println(err)
        return
    }
    _, err = fmt.Fprintln(conn, string(b))
    if err != nil {
        fmt.Println(err)
    }
}

ReadLogStash 是一个正在运行的 goroutine。如果对方关闭,我会得到 EOF。 ReadLogStash 中的一个好的实现是让它在收到 EOF 时每 X 秒尝试重新建立连接?

【问题讨论】:

  • ToLogStash 和 ReadLogStack 有什么关系?
  • @JimB 他们都通过了相同的连接。这个想法是发送不会因为 goroutine 而被读取阻塞。但也许这没有意义

标签: tcp go


【解决方案1】:

Go 有用于同步和通信的通道,使用它们!

使您的连接循环,并让它等待某种消息在频道上返回。

...
errCh := make(chan error)
for {
    lsconn, err = net.Dial("tcp", logStashHost)
    // check error!
    go ReadLogStash(lsconn, errCh)
    err = <-errCh
    if err != nil {
        // bad error
        break
    }
    // sleep to backoff on retries?
}
...

func ReadLogStash(conn net.Conn, errCh chan error) {
    _, err := io.Copy(os.Stderr, conn)
    if err != nil {
        fmt.Println(err)
    }
    // a nil error from io.Copy means you reached EOF.
    errCh <- err
}

除非您在 ReadLogStash 中拥有更多功能,否则您可能只使用 io.Copy inline,而忘记整个函数,但无论如何这种模式可能对您有用。

【讨论】:

  • 这会创建一堆 TCP 连接,不是吗?我只想一次打开一个...
  • 但是我会玩一个频道,把我想发送的东西放到一个频道上,然后有一个单独的 goroutine 来处理它们的发送 - 看看这对我来说是如何工作的。
  • 不,这将阻止&lt;-errCh,在当前连接关闭之前它不会有值。
  • 我知道这是旧的,但它缺少 go ReadLogStash(lsconn, errCh) 处的 go 语句,否则 ReadLosgStash 在 err = &lt;-errCh 之前完全执行。
【解决方案2】:

这是我最终的结果,频道是正确的方向:

if logStashHost != "" {
    lsc = make(chan *logrow.Record)
    go ToLogStash(lsc, logStashHost)
}
...
if lsc != nil {
   lsc <- rec
}
...
func ToLogStash(c chan *logrow.Record, logStashHost string) {
    var lsconn net.Conn
    var enc *json.Encoder
    var err error
    connect := func() {
        for {
            lsconn, err = net.Dial("tcp", logStashHost)
            if err == nil {
                enc = json.NewEncoder(lsconn)
                break
            }
            log.Println(err)
            time.Sleep(time.Second)
        }
    }
    connect()
    for r := range c {
        err = enc.Encode(r)
        if err != nil {
            lsconn.Close()
            log.Println(err)
            connect()
        }
    }
}

【讨论】:

  • 愤怒地注意到这不是不读,因为在这种情况下我不需要阅读。所以似乎写入错误足以关闭套接字并尝试启动一个新的
  • 等待通过 TCP 连接写入错误不是一个好主意,因为这是检测它关闭的唯一方法。在最好的情况下,在您收到错误之前需要额外的send如果您有数据等待额外的发送),最坏的情况是您等待网络超时。跨度>
猜你喜欢
  • 2020-06-07
  • 2015-05-02
  • 2010-12-30
  • 2021-02-18
  • 2014-12-10
  • 2014-07-15
  • 1970-01-01
  • 1970-01-01
  • 2017-03-24
相关资源
最近更新 更多