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 中好像会有内存泄露,应该是回收的地方做的不对。
问问大佬们这种咋处理。感谢。
1
reus 2020-03-09 21:16:01 +08:00
这不叫泄露,你代码本来就不限制并发量。
|
2
reus 2020-03-09 21:16:57 +08:00 1
|
3
guonaihong 2020-03-09 21:19:33 +08:00 1
刚刚看了你用的库的文档,例子里有 Stop 函数,ping 结束你调用下 Stop 函数看下呢。
|
4
Mohanson 2020-03-09 21:23:08 +08:00 via Android 1
生产者消费者模型找个教程看下吧,你没有限制消费者数量。
|
5
zhuyuefeng 2020-03-10 00:42:51 +08:00
请问具体 goroutine 泄露的表现是什么呢?在 pprof 中发现 无论 ping 任务是否结束 goroutine 都保持在一个很高的数吗?
还是在执行 ping 的任务时很多,执行完数量就回落了呢? 同样,如果在 pprof 的 goroutine 中会打印函数调用栈信息,可以发现究竟都在哪发生了阻塞,可以快速定位出造成阻塞的 goroutine 代码。 |
6
GreyYang 2020-03-10 09:20:58 +08:00
看了下 go-ping 这个库,猜测可能是并发量大导致了死锁,有 issue: https://github.com/sparrc/go-ping/issues/77,可以尝试按照 issue 描述的方式增加 channel 容量,看看能否解决.
|
7
matrix67 OP @zhuyuefeng #5 我主要是不确定,MultiPing 方法里面 for 死循环中,里面开 goroutine,这些 goroutine 会被回收吗,还是会泄露。我试试逻辑里面不去调用 ping,就直接把传入 PingIp 的值放入 ch 中,看看这种情况下是否会泄露。
@GreyYang #6 试过增加容量,还是有问题。应该不是。问题应该是 reus 所说的,要用一个 pool 池。 |
8
zunceng 2020-03-10 14:13:29 +08:00
就问你 发请求的函数没有 ctx 参数 心里虚不虚
|
9
hzzhzzdogee 2020-03-10 14:21:01 +08:00
没细看, 猜测要在 PingIP 函数加超时 context 吧, 同意楼上
|
10
zhuyuefeng 2020-03-10 14:22:53 +08:00
@matrix67 MultiPing 按照我的理解,是主 Goroutine 执行的,本身如果是死循环的话,主程序 不会终止。
此外就是楼上说到的,如果 ping 那边有阻塞的话,也会使得调用其的 goroutine 阻塞了。建议可以看看 ping 那边的超时参数 |
11
matrix67 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 |
12
matrix67 OP 好吧 查出来了,我自己写的有问题。
|
13
hzzhzzdogee 2020-03-10 16:22:06 +08:00
@matrix67 具体是什么问题呢? 我看了下 go-ping 这个库, 似乎直接设置下 pinger.Timeout 就行
|
15
matrix67 OP @hzzhzzdogee
@kuro1 我这个版本,写的不太好。MultiPing 这边 for 循环里面写了个 go PingIp,相当于无限消费者。所以内存爆炸了。 新写了一个版本,生产者读取 file.txt 送到一个 chan 里面去,然后 main 函数里面开 goroutine 作为消费者,消费 chan 里面的 ip 就行了。goroutine 的个数可以自己控制,也可以按照二楼老哥的方案,弄个池子。 |