這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
想寫個程式,監控目錄和檔案變化,原先目錄非常大,所以感覺要用goroutine對每個目錄派生一個goroutine進程,但程式在啟動並執行時候發現,開啟的目錄非常多,以致系統出錯,我們先來看看這個失敗的程式,目錄小是沒有問題的。
// main.gopackage mainimport (//"fmt""io/ioutil""log""os""path/filepath"//"regexp""runtime""strings""github.com/fsnotify/fsnotify")type Fm struct {Basedir stringWatcher *fsnotify.WatcherWdone chan boolDirchan chan string}func (fm *Fm) Init(basedir string) {fm.Basedir = filepath.FromSlash(basedir)var err errorfm.Watcher, err = fsnotify.NewWatcher()if err != nil {log.Fatal("create watcher error:", err)}fm.Wdone = make(chan bool)go func() {for {select {case event := <-fm.Watcher.Events://log.Println("event:", event)fm.process_event(event)case err := <-fm.Watcher.Errors:log.Println("watcher error:", err)}}}()fm.Dirchan = make(chan string, 1000)}func (fm *Fm) walkdir(path string) {//log.Println("basedir:", path)fm.Dirchan <- pathdir, err := ioutil.ReadDir(path)if err != nil {log.Fatal("opendir:", err)}for _, fi := range dir {fpath := filepath.FromSlash(path + "/" + fi.Name())if fi.IsDir() {if strings.HasPrefix(fi.Name(), ".") {continue}if strings.HasPrefix(fi.Name(), "..") {continue}if strings.Contains(fi.Name(), "lost+found") {continue}go fm.walkdir(fpath)} else {fm.Dirchan <- fpath}//log.Println("path:", fpath)//ch <- fpath}//}func (fm *Fm) lister() {var path stringfor {select {case path = <-fm.Dirchan:err := fm.Watcher.Add(path)if err != nil {log.Fatal("add watcher error:", err)}}}}func (fm *Fm) Start() {go fm.walkdir(fm.Basedir)go fm.lister()defer fm.Watcher.Close()<-fm.Wdone}func (fm *Fm) process_event(event fsnotify.Event) {switch event.Op {case fsnotify.Create:fm.Watcher.Add(event.Name)log.Println("create:", event.Name)case fsnotify.Rename, fsnotify.Remove:log.Println("remove:", event.Name)fm.Watcher.Remove(event.Name)case fsnotify.Write:log.Println("write:", event.Name)}}func main() {runtime.GOMAXPROCS(runtime.NumCPU() / 2)/* echo 50000000 > /proc/sys/fs/inotify/max_user_watches echo 327679 > /proc/sys/fs/inotify/max_queued_events*/filem := new(Fm)filem.Init(os.Args[1])filem.Start()}
上面程式有意思的地方是,遞迴目錄,用多線程進行通訊,但是目錄和檔案很多的時候,產生的線程也非常多。
下面來個簡單的例子,這個例子可以正常工作,速還不錯,140G的小檔案目錄,大約需要1.7G虛擬記憶體,實佔大約500m左右,由於和sersync效能相差太遠,所以暫時放棄了這個監控使用。
// main.gopackage mainimport ("fmt""log""os""path/filepath""github.com/fsnotify/fsnotify")//type MyWatcher *fsnotify.Watcherfunc doev(watcher *fsnotify.Watcher, event fsnotify.Event) {switch event.Op {case fsnotify.Create:watcher.Add(event.Name)log.Println("create:", event.Name)case fsnotify.Rename, fsnotify.Remove:log.Println("remove:", event.Name)watcher.Remove(event.Name)case fsnotify.Write:log.Println("write:", event.Name)}}func main() {watchdir := os.Args[1]var err errorwatcher, err := fsnotify.NewWatcher()if err != nil {log.Fatal(err)}defer watcher.Close()done := make(chan bool)go func() {for {select {case event := <-watcher.Events://log.Println("event:", event)doev(watcher, event)case err := <-watcher.Errors:log.Println("error:", err)}}}()err = watcher.Add(watchdir)if err != nil {log.Fatal(err)}err = filepath.Walk(watchdir, func(path string, info os.FileInfo, err error) error {err = watcher.Add(path)if err != nil {log.Fatal(err)}return nil})if err != nil {fmt.Printf("walk error [%v]\n", err)}<-done}