请教下golang goroutine泄露问题?

breadHood · · 3304 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

golang新手, 最近在写个练手的小项目, 需求是收集指定日志目录下最新的日志文件, 通过正则过滤日志内容, 把想要的信息存到一个map里. 但是这个程序在实际运行中我发现, 由于在catLog包里读取文件内容时里面我必须使用for死循环实现实时读取文件, 导致在getNewFile包里监控到的系统事件信息无法发送给catLog包,这样如果有新的日志文件产生, 它无法切换文件. 于是我在getNewFile包调用catLog包时启用goroutine, 这样就可以收到通知了. 结果导致每次读取文件新内容时, 程序都会启动一个新的goroutine调用catLog包, 并且不会自动退出,因为catLog包里是for死循环, 一直在累积增加. 请大佬们帮忙看下下面的代码, 该怎么修改可以控制goroutine增长, 并且还能实时监控最新的日志文件. ```golang package getNewFile import ( "fmt" "log" "regexp" catlog "study/prometheus/exporter_watcherLog/catLog" "time" "github.com/fsnotify/fsnotify" ) // 实时监控指定目录下文件并读取 func GetNewFile(pattren, logDir string) { watcher, err := fsnotify.NewWatcher() if err != nil { fmt.Println(err) return } defer watcher.Close() done := make(chan bool) re := regexp.MustCompile(pattren) // Compile 解析一个正则表达式,如果成功则返回一个可用于匹配文本的 Regexp 对象。如错误会panic go func() { for { select { case event := <-watcher.Events: isMatch := re.MatchString(event.Name) if isMatch { if event.Op&fsnotify.Create == fsnotify.Create { // 监控创建文件动作 fmt.Println(event.Name) go catlog.DealLog(event.Name) } if event.Op&fsnotify.Write == fsnotify.Write { // 监控写入文件动作 fmt.Println(event.Name) go catlog.DealLog(event.Name) } } case err := <-watcher.Errors: log.Println(err) } time.Sleep(time.Second * 1) } }() err = watcher.Add(logDir) // 监控指定目录 if err != nil { log.Fatal(err) } <-done } ``` ```golang package catlog import ( "fmt" "regexp" "runtime" "time" "sync" "github.com/hpcloud/tail" ) var AllAddrLoglist map[string]ClassLogInfo var Sm sync.Map var Notify = false type ClassLogInfo struct { Host string Time string Date string Cfc string } // 实时读取文件内容, 并筛选出host, date, time, cfc存入map中 func DealLog(file string) { config := tail.Config{ ReOpen: true, // 重新打开 Follow: true, // 是否跟随 Location: &tail.SeekInfo{Offset: 0, Whence: 2}, // 从文件哪个地方开始读 MustExist: false, // 文件不存在报错 Poll: true, // Logger: tail.DiscardingLogger, // 禁用日志记录 } tails, err := tail.TailFile(file, config) // 打开文件, 并用上面的配置 if err != nil { fmt.Printf("tail file failed, err: %v\n", err) return } var ( line *tail.Line ok bool ) ch := make(chan struct{}, 1) // 创建缓冲区大小, 控制并发, 最多发送x个消息就阻塞 // 正则匹配, 最后只筛选出host;date;time;cfc, 其余的跳过. pattern := `(^[0-9]+/[0-9]+/[0-9]+)\s+([0-9]+:[0-9]+:[0-9]+:[0-9]+)\s+[A-Z]+\s+\[.*\]\s+INFO\s+LSG.*LSG\s+svr\s+update:\s+cfc=([0-9]+),cpfc=[0-9]+,clc=[0-9]+,cplc=[0-9]+,load=[0-9]+,status=[0-9]+,addr=(\d+\.\d+\.\d+\.\d+-\d),inst=\d+\.\d+\.\d+\.\d+-\d` // re := regexp.MustCompile(pattern) // 两个唯一区别, 如错误直接panic re, err := regexp.Compile(pattern) // Compile 解析一个正则表达式,如果成功则返回一个可用于匹配文本的 Regexp 对象。如错误会返回一个错误 if err != nil { fmt.Println(err) } // 读取每行数据,最后写入到AllAddrLoglist中. for { ch <- struct{}{} // 写入消息到缓冲区 line, ok = <-tails.Lines if !ok { fmt.Printf("tail file close reopen, filename: %s\n", tails.Filename) time.Sleep(time.Second) continue } matchArr := re.FindStringSubmatch(line.Text) if matchArr != nil { // 新增判断,替代re.MatchString(line.Text),原有的影响性能 // 信息存入结构体 testI := ClassLogInfo{ Host: matchArr[4], Date: matchArr[1], Time: matchArr[2], Cfc: matchArr[3], } // 利用sync.map, 直接使用, 无需声明, 避免读写同一个map冲突 Sm.Store(matchArr[4], testI) <-ch // 从缓冲区读取消息 } } } ```

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3304 次点击  
加入收藏 微博
10 回复  |  直到 2023-03-21 23:46:54
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传