改进网络代码,而不使用goto语句来处理断开连接的情况。

huangapple go评论100阅读模式
英文:

Improve network code without using goto statement for broken connections cases

问题

如何在没有使用goto语句的情况下改进我的代码?

我的函数从服务器读取数据并将数据发送到另一个处理数据的函数中,为了处理断开连接的情况,我不得不添加一个goto语句,但我没有找到更好的方法。

你能给我一些建议吗?

func Reader(source string, proto string, chOutput chan string) {
init:
    fmt.Println("Conectando con Source:", source)
    conn, err := net.Dial(proto, source)
    if err != nil {
        fmt.Println("Error:", err.Error())
    }
    defer conn.Close()

    reader := bufio.NewReader(conn)

    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("Error:", err.Error())
            time.Sleep(1 * time.Second)
            goto init
        }
        fmt.Println("Enviando dato a Buffer:", line)
        chOutput <- line
    }
}

我的函数是一个goroutine:

func main(){
    mychan:= make(chan string)
    go Reader(source, proto, mychan)
    go Process(mychan)
    ...
}

使用goto语句或标签是解决重试连接的最佳方法吗?还有其他标准的方法吗?

英文:

How can I improve my code without a goto statement?

My function is reading from a server and sending data
to another function that process the data, I had to add a
goto statement in order to deal broken connection cases,
I didn't find a better way to do that.

Could you help me with some advices, please?

func Reader(source string, proto string, chOutput chan string) {
init:
	fmt.Println(&quot;Conectando con Source:&quot;, source)
	conn, err := net.Dial(proto, source)
	if err != nil {
		fmt.Println(&quot;Error:&quot;, err.Error())
	}
	defer conn.Close()

	reader := bufio.NewReader(conn)

	for {
		line, err := reader.ReadString(&#39;\n&#39;)
		if err != nil {
			fmt.Println(&quot;Error:&quot;, err.Error())
			time.Sleep(1 * time.Second)
			goto init
		}
		fmt.Println(&quot;Enviando dato a Buffer:&quot;, line)
		chOutput &lt;- line
	}
}

My function is a goroutine :

func main(){
    mychan:= make(chan string)
    go Reader(source, proto, mychan)
    go Process(mychan)
    ...

}

Is goto or a label the best way to solve retry connections?
Are there another standard way to do that?

答案1

得分: 3

您可以将错误处理交给调用者,并返回一个特殊的错误:

var errRetry = errors.New("retry")

func Reader(source string, proto string, chOutput chan<- string) error {
    fmt.Println("Conectando con Source:", source)
    conn, err := net.Dial(proto, source)
    if err != nil {
        return err
    }
    defer conn.Close()

    scanner := bufio.NewScanner(conn)
    for scanner.Scan() {
        line := scanner.Text()
        fmt.Println("Enviando dato a Buffer:", line)
        chOutput <- line
    }
    if err := scanner.Err(); err != nil {
        return errRetry
    }
    return nil
}

func main() {
    ch := make(chan string)
    go func() {
        for s := range ch {
            // 处理 s
            _ = s
        }
    }()
L:
    for {
        switch err := Reader("localhost:9020", "tcp4", ch); err {
        case errRetry:
            // 无需操作,进行重试
        case nil:
            // 如果连接正常关闭,是否要跳出循环?
            break L
        default:
            // 处理错误
            break L
        }
    }
    close(ch)
}

以上是您提供的代码的翻译结果。

英文:

You could make error handling the caller's problem and return a special error:

var errRetry = errors.New(&quot;retry&quot;)

func Reader(source string, proto string, chOutput chan&lt;- string) error {
	fmt.Println(&quot;Conectando con Source:&quot;, source)
	conn, err := net.Dial(proto, source)
	if err != nil {
		return err
	}
	defer conn.Close()

	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		line := scanner.Text()
		fmt.Println(&quot;Enviando dato a Buffer:&quot;, line)
		chOutput &lt;- line
	}
	if err := scanner.Err(); err != nil {
		return errRetry
	}
	return nil
}
    
func main() {
	ch := make(chan string)
	go func() {
		for s := range ch {
			// stuff with s
			_ = s
		}
	}()
L:
	for {
		switch err := Reader(&quot;localhost:9020&quot;, &quot;tcp4&quot;, ch); err {
		case errRetry:
			// do nothing to retry
		case nil:
			// do you want to break if the connection got closed gracefully?
			break L
		default:
			//handle err
			break L
		}
	}
	close(ch)
}

答案2

得分: 0

你可以通过引入一个外部循环来消除goto语句。如果打开连接时出现错误,继续执行外部循环。如果读取行时出现错误,则跳出内部循环。

在完成连接后关闭连接。defer语句直到函数返回才执行。

func Reader(source string, proto string, chOutput chan string) {
  for {
    fmt.Println("Conectando con Source:", source)
    conn, err := net.Dial(proto, source)
    if err != nil {
      fmt.Println("Error:", err.Error())
      time.Sleep(1 * time.Second)
      continue
    }
    reader := bufio.NewReader(conn)
    for {
      line, err := reader.ReadString('\n')
      if err != nil {
        fmt.Println("Error:", err.Error())
        conn.Close()
        time.Sleep(1 * time.Second)
        break
      }
      fmt.Println("Enviando dato a Buffer:", line)
      chOutput <- line
    }
  }
}

考虑在连接失败时使用指数退避:

func Reader(source string, proto string, chOutput chan string) {
  sleep := time.Second
  for {
    fmt.Println("Conectando con Source:", source)
    conn, err := net.Dial(proto, source)
    if err != nil {
      fmt.Println("Error:", err.Error())
      sleep *= 2 // 指数退避
      if sleep > time.Minute {
        sleep = time.Minute
      }
      time.Sleep(sleep)
      continue
    }
    sleep = time.Second // 成功后重置。
    reader := bufio.NewReader(conn)
    for {
      line, err := reader.ReadString('\n')
      if err != nil {
        fmt.Println("Error:", err.Error())
        conn.Close()
        time.Sleep(sleep)
        break
      }
      fmt.Println("Enviando dato a Buffer:", line)
      chOutput <- line
    }
  }
}
英文:

You can eliminate the goto by introducing an outer loop. Continue the outer loop if there's error opening the connection. Break out of the inner loop if there's an error reading lines.

Close the connection when you are done with it. The defer does not execute until the function returns.

func Reader(source string, proto string, chOutput chan string) {
  for {
	fmt.Println(&quot;Conectando con Source:&quot;, source)
	conn, err := net.Dial(proto, source)
	if err != nil {
		fmt.Println(&quot;Error:&quot;, err.Error())
		time.Sleep(1 * time.Second)
        continue
	}
	reader := bufio.NewReader(conn)
	for {
		line, err := reader.ReadString(&#39;\n&#39;)
		if err != nil {
			fmt.Println(&quot;Error:&quot;, err.Error())
            conn.Close()
			time.Sleep(1 * time.Second)
			break
		}
		fmt.Println(&quot;Enviando dato a Buffer:&quot;, line)
		chOutput &lt;- line
	}
  }
}

Consider using exponential backoff on connection failures:

func Reader(source string, proto string, chOutput chan string) {
  sleep := time.Second
  for {
	fmt.Println(&quot;Conectando con Source:&quot;, source)
	conn, err := net.Dial(proto, source)
	if err != nil {
		fmt.Println(&quot;Error:&quot;, err.Error())
        sleep *= 2 // exponential backoff
        if sleep &gt; time.Minute {
           sleep = time.Minute
        }
		time.Sleep(sleep)
        continue
	}
    sleep = time.Second // Reset on success.
	reader := bufio.NewReader(conn)
	for {
		line, err := reader.ReadString(&#39;\n&#39;)
		if err != nil {
			fmt.Println(&quot;Error:&quot;, err.Error())
            conn.Close()
			time.Sleep(sleep)
			break
		}
		fmt.Println(&quot;Enviando dato a Buffer:&quot;, line)
		chOutput &lt;- line
	}
  }
}

huangapple
  • 本文由 发表于 2014年10月13日 20:43:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/26340299.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定