gocron
gocron copied to clipboard
fix:任务超时导致内存泄露
没看出这段代码哪里导致内存泄露了,要怎么模拟内存泄露。
// 用这种方式kill, 子进程不会退出。
if err := cmd.Process.Kill(); err != nil {
logger.Errorf("Process kill pid:%d err:%v", cmd.Process.Pid, err)
}
如果任务超时,会执行 ctx.Done(),然后退出 ExecShell 函数同时结束resultChan接收,resultChan是没有缓冲的,会导致协程中写入resultChan一直阻塞。 复现步骤,构建一个运行时间稍微久的任务,并设置任务超时时间,到达超时时间后即可出现。 定位问题用的pprof,执行 go tool pprof http://localhost:6789/debug/pprof/goroutine,有协程阻塞在 output, err := cmd.CombinedOutput(),这个是正常的,还有的阻塞在 resultChan <- Result{string(output), err},这个就明显有问题了。
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL),这个不太清楚为什么用这个,没有用cmd.Process.Kill(),我在mac上用第一种方式无法kill,第二种可以。
ExecShell返回的数据我觉得需要及时返回,CombinedOutput() 会导致运行时间久或输出多的任务占用很大内存
在 2021年4月14日 @.***> 写道:
没看出这段代码哪里导致内存泄露了,要怎么模拟内存泄露。 // 用这种方式kill, 子进程不会退出。 if err := cmd.Process.Kill(); err != nil { logger.Errorf("Process kill pid:%d err:%v", cmd.Process.Pid, err) } — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.
任务超时后在ctx.Done()里面就return了,不会有机会执行到这里了
在 2021年4月14日 @.***> 写道:
这里读取了chan,不会造成阻塞 case result := <-resultChan: return result.output, result.err } — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.
写了个demo
// 模拟任务执行,任务耗时3s,超时时间1s, func main() { cmd := exec.Command("/bin/bash", "-c", "sleep 3") ctx, _ := context.WithTimeout(context.Background(), time.Second)
ch := make(chan string) go func() { bs, err := cmd.CombinedOutput() fmt.Println("bs", string(bs), "err", err) // 任务超时这里会一直阻塞 ch <- string(bs) fmt.Println("routine down!") }()
select { case str := <-ch: fmt.Println("receive:", str) case <-ctx.Done(): cmd.Process.Kill() fmt.Println("timeout") }
time.Sleep(time.Second * 10) }
在 2021年4月14日 @.***> 写道:
这里读取了chan,不会造成阻塞 case result := <-resultChan: return result.output, result.err } — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.
的确存在内存泄露的情况,这块代码要重写,可能也会出现竞态条件,并发读写cmd.Process。
这段代码的作用是kill掉所有子进程,比如A创建了子进程B, B创建了子进程C。 cmd.Process.Kill()只对A有效,B和C进程仍会继续运行。
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)