【问题标题】:golang: strategies to prevent connection reset by peer errorsgolang:防止对等错误重置连接的策略
【发布时间】:2018-01-02 02:01:15
【问题描述】:

该程序同时生成许多 goroutines (getStock),我相信这会导致远程服务器立即断开连接。我不是在尝试创建 DOS,但我仍然想积极地获取数据而不会出现“连接重置”错误。

有哪些策略最多只能有 N 个(例如 20)个同时连接? golang Http 客户端中是否有内置的 GET 请求队列?我还在学习,如果有更好的设计模式适合这种类型的代码,那就太好了。

输出

$ go run s1w.go 
sl(size): 1280
body: "AAPL",17.92
body: "GOOG",32.13
body: "FB",42.02
body: "AMZN",195.83
body: "GOOG",32.13
body: "AMZN",195.83
body: "GOOG",32.13
body: "FB",42.02
body: "AAPL",17.92
2017/07/26 00:01:23 NFLX: Get http://goanuj.freeshell.org/go/NFLX.txt: read tcp 192.168.86.28:56674->205.166.94.30:80: read: connection reset by peer
2017/07/26 00:01:23 AAPL: Get http://goanuj.freeshell.org/go/AAPL.txt: read tcp 192.168.86.28:56574->205.166.94.30:80: read: connection reset by peer
2017/07/26 00:01:23 NFLX: Get http://goanuj.freeshell.org/go/NFLX.txt: read tcp 192.168.86.28:56760->205.166.94.30:80: read: connection reset by peer
2017/07/26 00:01:23 FB: Get http://goanuj.freeshell.org/go/FB.txt: read tcp 192.168.86.28:56688->205.166.94.30:80: read: connection reset by peer
2017/07/26 00:01:23 AMZN: Get http://goanuj.freeshell.org/go/AMZN.txt: read tcp 192.168.86.28:56689->205.166.94.30:80: read: connection reset by peer
2017/07/26 00:01:23 AAPL: Get http://goanuj.freeshell.org/go/AAPL.txt: read tcp 192.168.86.28:56702->205.166.94.30:80: read: connection reset by peer

s1.go

package main
import (
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
        "time"
)

// https://www.youtube.com/watch?v=f6kdp27TYZs (15m)
// Generator: function that returns a channel
func getStocks(sl []string) <-chan string {
        c := make(chan string)
        for _, s := range sl {
                go getStock(s, c)
        }
        return c
}

func getStock(s string, c chan string) {
        resp, err := http.Get("http://goanuj.freeshell.org/go/" + s + ".txt")
        if err != nil {
                log.Printf(s + ": " + err.Error())
                c <- err.Error() // channel send
                return
        }
        body, _ := ioutil.ReadAll(resp.Body)
        resp.Body.Close() // close ASAP to prevent too many open file desriptors
        val := string(body)
        //fmt.Printf("body: %s", val)
        c <- val // channel send
        return
}

func main() {
        start := time.Now()
        var sl = []string{"AAPL", "AMZN", "GOOG", "FB", "NFLX"}
        // creates slice of 1280 elements
        for i := 0; i < 8; i++ {
                sl = append(sl, sl...)
        }
        fmt.Printf("sl(size): %d\n", len(sl))

        // get channel that returns only strings
        c := getStocks(sl)
        for i := 0; i < len(sl); i++ {
                fmt.Printf("%s", <-c) // channel recv
        }

        fmt.Printf("main: %.2fs elapsed.\n", time.Since(start).Seconds())
}

【问题讨论】:

    标签: http go http-get task-queue flooding


    【解决方案1】:

    不要为每个请求创建新的 goroutine,而是在程序启动时创建一个固定池并通过共享通道传递订单。每个订单都是一个结构,对应于当前传递给getStock 的参数。如果您需要能够杀死游泳池,事情会变得更加复杂,但这仍然不是那么难......

    基本上,您的新处理程序将是一个循环,从所有处理程序共享的通道中读取订单,执行它,然后在订单的响应通道上发送结果。

    【讨论】:

    • 杀死池很容易:关闭你传递作业的通道。
    • 您有任何示例代码或链接来说明如何执行此操作吗?
    • 看看this article about worker pools,它解释了一切,比你需要的要详细得多:)
    【解决方案2】:

    您需要使用缓冲通道来限制并行操作的数量。在循环中开始一个新的 goroutine 之前,你需要发送到这个通道并在调用完成后从它接收,所以它会释放一个地方并且新的请求将能够开始。查看您修改后的代码on playground

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-07
      • 2017-06-09
      相关资源
      最近更新 更多