Heim  >  Artikel  >  Backend-Entwicklung  >  So implementieren Sie die Dateiüberwachung in Golang

So implementieren Sie die Dateiüberwachung in Golang

青灯夜游
青灯夜游Original
2023-02-20 10:15:374619Durchsuche

In Golang können Sie fsnotify verwenden, um die Dateiüberwachung zu implementieren. fsnotify ist ein plattformübergreifendes Dateisystemüberwachungstool in der Go-Sprache, das eine kanalbasierte, plattformübergreifende Echtzeitüberwachungsschnittstelle implementiert. Golang kann Dateien über fsnotify überwachen und das Programm durch Dateiänderungen neu starten.

So implementieren Sie die Dateiüberwachung in Golang

Die Betriebsumgebung dieses Tutorials: Windows 10-System, GO Version 1.18, Dell G3-Computer.

In Golang können Sie fsnotify verwenden, um die Dateiüberwachung zu implementieren.

golang überwacht Dateien über fsnotify und startet das Programm bei Dateiänderungen neu.

Plattformübergreifendes Dateisystem-Überwachungstool der Go-Sprache – fsnotify

Im Linux-Kernel ist Inotify ein Mechanismus, der verwendet wird, um User-Space-Programme über Dateisystemänderungen zu benachrichtigen. Es überwacht Dateisystemänderungen wie das Erstellen, Ändern, Löschen usw. von Dateien und kann Anwendungen über entsprechende Ereignisse benachrichtigen.

Inotify kann sowohl Dateien als auch Verzeichnisse überwachen. Bei der Überwachung eines Verzeichnisses können das Verzeichnis und jedes Unterverzeichnis und jede Datei im Verzeichnis gleichzeitig überwacht werden. Der Standardbibliotheks-Systemaufruf von Golang implementiert diesen Mechanismus.

Zur weiteren Erweiterung und Abstraktion implementiert das Paket github.com/fsnotify/fsnotify eine kanalbasierte, plattformübergreifende Echtzeit-Überwachungsschnittstelle.

Verwendung des fsnotify-Tools

1. Laden Sie die benötigten Pakete herunter

go get github.com/fsnotify/fsnotify

2. Verwenden Sie fsnotify, um Dateien zu überwachen

1

2

3

4

5

6

19

20

21

22

. 23.

24.

25.

26.

27.

28.

29

3 5

36

37

38

39

51

52

53

54

55

56

57

58

59

60

package main;

import (

"github.com/fsnotify/fsnotify" code><p class="line number37 index36 alt2"></p> <code class="js leerzeichen"> "log"

"fmt"

)

  

func main() {

    //创建一个监控对象

    watch, err := fsnotify.NewWatcher();

    if err != nil {

        log.Fatal(err);

    }

    defer watch.Close();

    //添加要监控的对象,文件或文件夹

    err = watch.Add("./tmp");

    if err != nil {

        log.Fatal(err);

    }

    //我们另启一个goroutine来处理监控对象的事件

    go func() {

        for {

            select {

            case ev :=

                {

                    //判断事件发生的类型,如下5种

                    // Create 创建

                    // Write 写入

                    // Remove 删除

                    // Rename 重命名

                    // Chmod 修改权限

                    if ev.Op&fsnotify.Create == fsnotify.Create {

                        log.Println("创建文件 : ", ev.Name);

                    }

                    if ev.Op&fsnotify.Write == fsnotify.Write {

                        log.Println("写入文件 : ", ev.Name);

                    }

                    if ev.Op&fsnotify.Remove == fsnotify.Remove {

                        log.Println("删除文件 : ", ev.Name);

                    }

                    if ev.Op&fsnotify.Rename == fsnotify.Rename {

                        log.Println("重命名文件 : ", ev.Name);

                    }

                    if ev.Op&fsnotify.Chmod == fsnotify.Chmod {

                        log.Println("修改权限 : ", ev.Name);

                    }

                }

            case err :=

                {

                    log.Println("error : ", err);

                    return;

                }

            }

        }

    }();

  

    //循环

    select {};

}

Die Testergebnisse lauten wie folgt:

So implementieren Sie die Dateiüberwachung in Golang

Alle unsere Vorgänge im tmp-Verzeichnis wurden erfasst, aber es gibt ein Problem mit fsnotify. Es kann uns nicht dabei helfen, die Vorgangsereignisse von Unterverzeichnissen und Enkelverzeichnissen rekursiv zu erfassen von uns selbst umgesetzt.

Ein weiteres Problem besteht darin, dass, wenn wir den Ordnernamen ändern, der event.Name in fsnotify immer noch der ursprüngliche Dateiname ist, was erfordert, dass wir zuerst die vorherige Überwachung entfernen und dann eine neue Überwachung im Umbenennungsereignis hinzufügen.

Geändert wie folgt:

1

2

3

4

5

6

7

8

9

10

11

12

13

. 14.

15.

16.

17.

18

2 6

27

28

29

30

42

43

44

45

46

47

59

60

61

62

63

64

7 6

77

78

79

80

81

82

83

84

85

86

87

88

Code>

import (

    "github.com/fsnotify/fsnotify"

    "fmt"

    "path/filepath"

    "os"

)

  

type Watch struct {

    watch *fsnotify.Watcher;

}

  

//监控目录

func (w *Watch) watchDir(dir string) {

    //通过Walk来遍历目录下的所有子目录

    filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {

        //这里判断是否为目录,只需监控目录即可

        //目录下的文件也在监控范围内,不需要我们一个一个加

        if info.IsDir() {

            path, err := filepath.Abs(path);

            if err != nil {

                return err;

            }

            err = w.watch.Add(path);

            if err != nil {

                return err;

            }

            fmt.Println("监控 : ", path);

        }

        return nil;

    });

    go func() {

        for {

            select {

            case ev :=

                {

                    if ev.Op&fsnotify.Create == fsnotify.Create {

                        fmt.Println("创建文件 : ", ev.Name);

                        //这里获取新创建文件的信息,如果是目录,则加入监控中

                        fi, err := os.Stat(ev.Name);

                        if err == nil && fi.IsDir() {

                            w.watch.Add(ev.Name);

                            fmt.Println("添加监控 : ", ev.Name);

                        }

                    }

                    if ev.Op&fsnotify.Write == fsnotify.Write {

                        fmt.Println("写入文件 : ", ev.Name);

                    }

                    if ev.Op&fsnotify.Remove == fsnotify.Remove {

                        fmt.Println("删除文件 : ", ev.Name);

                        //如果删除文件是目录,则移除监控

                        fi, err := os.Stat(ev.Name);

                        if err == nil && fi.IsDir() {

                            w.watch.Remove(ev.Name);

                            fmt.Println("删除监控 : ", ev.Name);

                        }

                    }

                    if ev.Op&fsnotify.Rename == fsnotify.Rename {

                        fmt.Println("重命名文件 : ", ev.Name);

                        //如果重命名文件是目录,则移除监控

                        //注意这里无法使用os.Stat来判断是否是目录了

                        //因为重命名后,go已经无法找到原文件来获取信息了

                        //所以这里就简单粗爆的直接remove好了

                        w.watch.Remove(ev.Name);

                    }

                    if ev.Op&fsnotify.Chmod == fsnotify.Chmod {

                        fmt.Println("修改权限 : ", ev.Name);

                    }

                }

            case err :=

                {

                    fmt.Println("error : ", err);

                    return;

                }

            }

        }

    }();

}

  

func main() {

    watch, _ := fsnotify.NewWatcher()

    w := Watch{

        watch: watch,

    }

    w.watchDir("./tmp");

    select {};

}

Die Testergebnisse lauten wie folgt:

So implementieren Sie die Dateiüberwachung in Golang

Nach dem obigen Beispiel verwenden wir fsnotify, um eine Überwachungskonfigurationsdatei zu schreiben. Wenn die Konfigurationsdatei geändert wird, wird der Dienst neu gestartet.

Wir schreiben zunächst ein exe-Programm, das ausgeführt werden kann. Der server.go-Code lautet wie folgt:

1

2

3

4

5

6

7

8

9

. 10

21

22

23

24

25.

26.

27.

28.

29.

30.

31.

32.

33 37

38

39

40

41

42

43

54

5 5

56

57

58

package main;

import (

"io/ioutil"

code>"log"

"encoding/json"

"net"

"fmt"

"os"

"os/signal"

)

const (

confFilePath = ./conf/conf.json";

) code><p class="line number49 index48 alt2"></p> <code class="js rooms">

//我们这里只是演示,配置项只设置一个

type Conf struct {

    Port int `json:port`;

}

  

func main() {

    //读取文件内容

    data, err := ioutil.ReadFile(confFilePath);

    if err != nil {

        log.Fatal(err);

    }

    var c Conf;

    //解析配置文件

    err = json.Unmarshal(data, &c);

    if err != nil {

        log.Fatal(err);

    }

    //根据配置项来监听端口

    lis, err := net.Listen("tcp", fmt.Sprintf(":%d", c.Port));

    if err != nil {

        log.Fatal(err);

    }

    log.Println("server start");

    go func() {

        ch := make(chan os.Signal);

        //获取程序退出信号

        signal.Notify(ch, os.Interrupt, os.Kill);

        

        log.Println("server exit");

        os.Exit(1);

    }();

    for {

        conn, err := lis.Accept();

        if err != nil {

            continue;

        }

        go func(conn net.Conn) {

            defer conn.Close();

            conn.Write([]byte("hellon"));

        }(conn);

    }

}

使用如下命令,编译成exe文件

1

> go build server.go

Der Code der Überwachungsdatei fsnotify3.go lautet wie folgt:

1

2

3

4

5

6

7

8

9

10

11

12

24

25

26

27

28

52

53

54

55

56

57

58

59

60

61

62 8

69

70

71

72

73

85

86

87

88

89

90

9 1

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

package main;package main;

  

import (

    "github.com/fsnotify/fsnotify"

    "log"

    "fmt"

    "os/exec"

    "regexp"

    "strconv"

    "bytes"

    "errors"

    "os"

    "path/filepath"

)

  

const (

    confFilePath = "./conf";

)

  

//获取进程ID

func getPid(processName string) (int, error) {

    //通过wmic process get name,processid | findstr server.exe获取进程ID

    buf := bytes.Buffer{};

    cmd := exec.Command("wmic", "process", "get", "name,processid");

    cmd.Stdout = &buf;

    cmd.Run();

    cmd2 := exec.Command("findstr", processName);

    cmd2.Stdin = &buf;

    data, _ := cmd2.CombinedOutput();

    if len(data) == 0 {

        return -1, errors.New("not find");

    }

  

import (🎜

    "github.com/fsnotify/fsnotify"🎜

     "log"🎜

     "fmt"🎜

    "os/exec" 🎜

    "regexp"🎜

    "strconv"🎜

    "bytes"🎜

    "errors"🎜

    "os "🎜

    "path/filepath"🎜)🎜

  🎜

const (🎜

    confFilePath = ./conf";🎜)🎜

  🎜

//获取进程ID🎜

func getPid(processName string) (int, error) {🎜

     //通过wmic-Prozess get name,processid | findstr server.exe获取进程ID🎜

    buf := Bytes. Buffer{};🎜

    cmd := exec.Command("wmic", "process", "get", "name,processid");🎜

    cmd.Stdout = &buf;🎜

    cmd.Run();🎜

    cmd2 : = exec.Command("findstr", processName);🎜

    cmd2.Stdin = &buf;🎜

    data, _ := cmd2.CombinedOutput();🎜

    if len(data) == 0 {🎜

        return -1,errors.New( "not find");🎜

    }🎜

    info := string(data);

    //这里通过正则把进程id提取出来

    reg := regexp.MustCompile(`[0-9]+`);

    pid := reg.FindString(info);

    return strconv.Atoi(pid);

}

  

//启动进程

func startProcess(exePath string, args []string) error {

    attr := &os.ProcAttr{

        //files指定新进程继承的活动文件对象

        //前三个分别为,标准输入、标准输出、标准错误输出

        Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},

        //新进程的环境变量

        Env: os.Environ(),

    }

  

    p, err := os.StartProcess(exePath, args, attr);

    if err != nil {

        return err;

    }

    fmt.Println(exePath, "进程启动");

    p.Wait();

    return nil;

}

  

func main() {

    //创建一个监控对象

    watch, err := fsnotify.NewWatcher();

    if err != nil {

        log.Fatal(err);

    }

    defer watch.Close();

    //添加要监控的文件

    err = watch.Add(confFilePath);

    if err != nil {

        log.Fatal(err);

    }

    //我们另启一个goroutine来处理监控对象的事件

    go func() {

        for {

            select {

            case ev :=

                {

                    //我们只需关心文件的修改

                    if ev.Op&fsnotify.Write == fsnotify.Write {

                        fmt.Println(ev.Name, "文件写入");

                        //查找进程

                        pid, err := getPid("server.exe");

                        //获取运行文件的绝对路径

                        exePath, _ := filepath.Abs("./server.exe")

                        if err != nil {

                            //启动进程

                            go startProcess(exePath, []string{});

                        } else {

                            //找到进程,并退出

                            process, err := os.FindProcess(pid);

                            if err == nil {

                                //让进程退出

                                process.Kill();

                                fmt.Println(exePath, "进程退出");

                            }

                            //启动进程

                            go startProcess(exePath, []string{});

                        }

                    }

                }

            case err :=

                {

                    fmt.Println("error : ", err);

                    return;

                }

            }

        }

    }();

  

    //循环

    select {};

}

Wir führen die Datei fsnotify3.go aus, um unsere Konfigurationsdatei zu überwachen

So implementieren Sie die Dateiüberwachung in Golang

Wie Sie auf dem Bild oben sehen können, wird der Vorgang ausgeführt, wenn wir die Portnummer in der Konfigurationsdatei ändern Zuerst getötet und dann einen anderen Prozess gestartet.

So implementieren Sie die Dateiüberwachung in Golang

Empfohlenes Lernen: Golang-Tutorial

Das obige ist der detaillierte Inhalt vonSo implementieren Sie die Dateiüberwachung in Golang. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn