package main
import (
	"bufio"
	"fmt"
	"github.com/sparrc/go-ping"
	"log"
	"os"
	"time"
)
type PingRestult struct {
	ip   string
	time time.Duration
}
func MultiPing(urls []string, ch chan PingRestult) {
	for {
		for _, url := range urls {
			go PingIp(url, ch)
		}
		time.Sleep(1 * time.Second)
	}
}
func GetResult(ch chan PingRestult) {
	for {
		fmt.Println(<-ch)
	}
}
func PingIp(ip string, ch chan PingRestult) {
	pinger, err := ping.NewPinger(ip)
	pinger.SetPrivileged(true)
	if err != nil {
		panic(err)
	}
	pinger.Count = 1
	pinger.Timeout = time.Second * 1
	pinger.Run()
	// blocks until finished
	stats := pinger.Statistics()
	result := PingRestult{ip, stats.AvgRtt}
	ch <- result
}
func main() {
	file, err := os.Open("file.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	ch := make(chan PingRestult, len(lines))
	defer close(ch)
	go GetResult(ch)
	MultiPing(lines, ch)
	select {}
}
在 file.txt 有 ip 列表,取出来,放在一个死循环中得到 ping 的结果。
但是 MultiPing 中好像会有内存泄露,应该是回收的地方做的不对。
问问大佬们这种咋处理。感谢。
|  |      1reus      2020-03-09 21:16:01 +08:00 这不叫泄露,你代码本来就不限制并发量。 | 
|  |      2reus      2020-03-09 21:16:57 +08:00  1 | 
|  |      3guonaihong      2020-03-09 21:19:33 +08:00  1 刚刚看了你用的库的文档,例子里有 Stop 函数,ping 结束你调用下 Stop 函数看下呢。 | 
|  |      4Mohanson      2020-03-09 21:23:08 +08:00 via Android  1 生产者消费者模型找个教程看下吧,你没有限制消费者数量。 | 
|      5zhuyuefeng      2020-03-10 00:42:51 +08:00 请问具体 goroutine 泄露的表现是什么呢?在 pprof 中发现 无论 ping 任务是否结束 goroutine 都保持在一个很高的数吗? 还是在执行 ping 的任务时很多,执行完数量就回落了呢? 同样,如果在 pprof 的 goroutine 中会打印函数调用栈信息,可以发现究竟都在哪发生了阻塞,可以快速定位出造成阻塞的 goroutine 代码。 | 
|  |      6GreyYang      2020-03-10 09:20:58 +08:00 看了下 go-ping 这个库,猜测可能是并发量大导致了死锁,有 issue: https://github.com/sparrc/go-ping/issues/77,可以尝试按照 issue 描述的方式增加 channel 容量,看看能否解决. | 
|  |      7matrix67 OP @zhuyuefeng #5 我主要是不确定,MultiPing 方法里面 for 死循环中,里面开 goroutine,这些 goroutine 会被回收吗,还是会泄露。我试试逻辑里面不去调用 ping,就直接把传入 PingIp 的值放入 ch 中,看看这种情况下是否会泄露。  @GreyYang #6 试过增加容量,还是有问题。应该不是。问题应该是 reus 所说的,要用一个 pool 池。 | 
|  |      8zunceng      2020-03-10 14:13:29 +08:00 就问你 发请求的函数没有 ctx 参数 心里虚不虚 | 
|  |      9hzzhzzdogee      2020-03-10 14:21:01 +08:00 没细看, 猜测要在 PingIP 函数加超时 context 吧,  同意楼上 | 
|      10zhuyuefeng      2020-03-10 14:22:53 +08:00 @matrix67 MultiPing 按照我的理解,是主 Goroutine 执行的,本身如果是死循环的话,主程序 不会终止。 此外就是楼上说到的,如果 ping 那边有阻塞的话,也会使得调用其的 goroutine 阻塞了。建议可以看看 ping 那边的超时参数 | 
|  |      11matrix67 OP @zunceng  @hzzhzzdogee #9 @zhuyuefeng #10 超时那边应该是 pinger.Timeout = time.Second * 1 这个吧,我设置了。 报告各位大佬,首先我的第一个实现版本是有问题。 现在我按照 http://jmoiron.net/blog/limiting-concurrency-in-go/ 这个里面的用法,以及用了 workerpool 的用法,我发现是不是这个 go-ping 库有问题啊,我把 PingIp 这个函数变为直接打印出来,就啥错误都没了。https://pastebin.com/89KknAMV | 
|  |      12matrix67 OP 好吧 查出来了,我自己写的有问题。 | 
|  |      13hzzhzzdogee      2020-03-10 16:22:06 +08:00 @matrix67 具体是什么问题呢? 我看了下 go-ping 这个库, 似乎直接设置下 pinger.Timeout 就行 | 
|  |      15matrix67 OP @hzzhzzdogee  @kuro1 我这个版本,写的不太好。MultiPing 这边 for 循环里面写了个 go PingIp,相当于无限消费者。所以内存爆炸了。 新写了一个版本,生产者读取 file.txt 送到一个 chan 里面去,然后 main 函数里面开 goroutine 作为消费者,消费 chan 里面的 ip 就行了。goroutine 的个数可以自己控制,也可以按照二楼老哥的方案,弄个池子。 |