Maison  >  Article  >  développement back-end  >  Comment implémenter la surveillance des fichiers dans Golang

Comment implémenter la surveillance des fichiers dans Golang

青灯夜游
青灯夜游original
2023-02-20 10:15:374619parcourir

Dans Golang, vous pouvez utiliser fsnotify pour implémenter la surveillance des fichiers. fsnotify est un outil de surveillance du système de fichiers multiplateforme dans le langage Go, qui implémente une interface de surveillance en temps réel multiplateforme basée sur les canaux ; Golang peut surveiller les fichiers via fsnotify et redémarrer le programme en modifiant les fichiers.

Comment implémenter la surveillance des fichiers dans Golang

L'environnement d'exploitation de ce tutoriel : système Windows 10, GO version 1.18, ordinateur Dell G3.

Dans Golang, vous pouvez utiliser fsnotify pour implémenter la surveillance des fichiers.

golang surveille les fichiers via fsnotify et redémarre le programme via les modifications de fichiers.

Outil de surveillance du système de fichiers multiplateforme en langage Go - fsnotify

Dans le noyau Linux, Inotify est un mécanisme utilisé pour informer les programmes de l'espace utilisateur des modifications du système de fichiers. Il surveille les modifications du système de fichiers, telles que la création, la modification, la suppression, etc. de fichiers, et peut notifier les applications des événements correspondants.

Inotify peut surveiller à la fois les fichiers et les répertoires. Lors de la surveillance d'un répertoire, il peut surveiller le répertoire ainsi que chaque sous-répertoire et fichier du répertoire en même temps. L'appel système de la bibliothèque standard de Golang implémente ce mécanisme.

Afin d'élargir et d'abstraire davantage, le package github.com/fsnotify/fsnotify implémente une interface de surveillance en temps réel multiplateforme basée sur les canaux.

Utilisation de l'outil fsnotify

1. Téléchargez les packages dont nous avons besoin

go get github.com/fsnotify/fsnotify

2 Utilisez fsnotify pour surveiller les fichiers

1

2

3

4.

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

3 5

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

5 1

52

53

54

55

56

57

58

59

60

paquet principal ;package main;

  

import (

    "github.com/fsnotify/fsnotify"

    "log"

    "fmt"

🎜🎜 importer (🎜🎜 "github.com/fsnotify/fsnotify" code>🎜🎜 "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 {};

}

Les résultats du test sont les suivants :

Comment implémenter la surveillance des fichiers dans Golang

Toutes nos opérations dans le répertoire tmp ont été capturées, mais il y a un problème avec fsnotify. Il ne peut pas nous aider à capturer les événements d'opération des sous-répertoires et des répertoires petits-fils de manière récursive. mis en œuvre par nous-mêmes.

Un autre problème est que lorsque nous modifions le nom du dossier, l'event.Name dans fsnotify est toujours le nom du fichier d'origine, ce qui nous oblige à d'abord supprimer la surveillance précédente, puis à ajouter une nouvelle surveillance dans l'événement rename.

Modifié comme suit :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

2 6

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

4 2

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

package principal;package main;

  

import (

🎜🎜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 {};

}

Les résultats du test sont les suivants :

Comment implémenter la surveillance des fichiers dans Golang

Après l'exemple ci-dessus, nous utilisons fsnotify pour écrire un fichier de configuration de surveillance. Si le fichier de configuration est modifié, le service sera redémarré.

Nous écrivons d'abord un programme exe qui peut être exécuté. Le code server.go est le suivant :

1

2

3

4

5

6

7

8.

9

​​

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

3 7

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

5 5

56

57

58

package main;package main;

  

import (

    "io/ioutil"

    "log"

    "encoding/json"

    "net"

    "fmt"

    "os"

    "os/signal"

)

  

const (

    confFilePath = "./conf/conf.json";

)

 

🎜🎜import ( 🎜 🎜 "io/ioutil"🎜🎜 code>"log"🎜🎜 "encoding/json"🎜🎜 "net"🎜🎜 "fmt"🎜🎜 "os"🎜🎜 "os/signal"🎜🎜)🎜🎜 🎜🎜<code class="js plain">const (🎜🎜 confFilePath = "./conf/conf.json";🎜🎜) code>🎜🎜<code class="js space"> 🎜

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

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

Le code fsnotify3.go du fichier de surveillance est le suivant :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

4 1

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

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");

    }

  

importer (🎜

    "github.com/fsnotify/fsnotify"🎜

     "log"🎜

     "fmt"🎜

    "os/exec" 🎜

    "regexp"🎜

    "strconv"🎜

    "octets"🎜

    "erreurs"🎜

    "os "🎜

    "chemin/chemin de fichier"🎜)🎜

  🎜

const (🎜

    confFilePath = "./conf";🎜)🎜

  🎜

//获取进程ID🎜

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

     //Le processus wmic obtient le nom, l'ID du processus | findstr server.exe获取进程ID🎜

    buf := octets. Buffer{};🎜

    cmd := exec.Command("wmic", "processus", "get", "nom,idprocessus");🎜

    cmd.Stdout = &buf;🎜

    cmd.Run();🎜

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

    cmd2.Stdin = &buf;🎜

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

    if len(data) == 0 {🎜

        retour -1, erreurs.Nouveau( "pas trouvé");🎜

    }🎜

    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 {};

}

Nous exécutons le fichier fsnotify3.go pour surveiller notre fichier de configuration

Comment implémenter la surveillance des fichiers dans Golang

Comme vous pouvez le voir sur l'image ci-dessus, lorsque nous modifions le numéro de port dans le fichier de configuration, le processus sera tué en premier, puis démarrez un autre processus.

Comment implémenter la surveillance des fichiers dans Golang

Apprentissage recommandé : Tutoriel Golang

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn