2017-07-26 37 views
0

该程序同时产生许多goroutines(getStock),我相信这会导致远程服务器立即丢弃连接。我不是想创建一个DOS,但我仍然想要积极获取数据而不会发生“连接重置”错误。golang:防止同级错误重置连接的策略

什么是一些策略最多只有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()) 
} 

回答

1

相反旋转了新够程为每个请求的,建立一个固定的池时,你的程序启动,在经过过订单共享频道。每个订单都是与当前传递给getStock的参数相对应的结构。事情变得更复杂,如果你需要能够杀死池,但它仍然不是那么难......

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

+0

杀死池很容易:关闭你传递作业的渠道。 – Adrian

+0

你有任何示例代码或链接显示如何做到这一点? – AG1

+1

看看[这篇关于工作者池的文章](https://gobyexample.com/worker-pools),它可以解释所有东西,比你需要的更详细:) –

0

您需要使用缓冲通道来限制并行操作的数量。在循环中启动一个新的goroutine之前,您需要发送到此通道并在完成通话后从其接收,因此它将释放一个位置并且新的请求将能够启动。查看你的修改代码on playground