>  기사  >  백엔드 개발  >  golang에서 파일 모니터링을 구현하는 방법

golang에서 파일 모니터링을 구현하는 방법

青灯夜游
青灯夜游원래의
2023-02-20 10:15:374693검색

golang에서는 fsnotify를 사용하여 파일 모니터링을 구현할 수 있습니다. fsnotify는 채널 기반의 크로스 플랫폼 실시간 모니터링 인터페이스를 구현하는 Go 언어의 크로스 플랫폼 파일 시스템 모니터링 도구입니다. golang은 fsnotify를 통해 파일을 모니터링하고 파일 변경을 통해 프로그램을 다시 시작할 수 있습니다.

golang에서 파일 모니터링을 구현하는 방법

이 튜토리얼의 운영 환경: Windows 10 시스템, GO 버전 1.18, Dell G3 컴퓨터.

golang에서는 fsnotify를 사용하여 파일 모니터링을 구현할 수 있습니다.

golang은 fsnotify를 통해 파일을 모니터링하고 파일 변경을 통해 프로그램을 다시 시작합니다.

Go 언어 크로스 플랫폼 파일 시스템 모니터링 도구 - fsnotify

Linux 커널에서 Inotify는 사용자 공간 프로그램에 파일 시스템 변경 사항을 알리는 데 사용되는 메커니즘입니다. 파일 생성, 수정, 삭제 등의 파일 시스템 변경 사항을 모니터링하고 해당 이벤트를 애플리케이션에 알릴 수 있습니다.

Inotify는 파일과 디렉터리를 모두 모니터링할 수 있습니다. 디렉터리를 모니터링할 때 디렉터리와 해당 디렉터리의 각 하위 디렉터리 및 파일을 동시에 모니터링할 수 있습니다. Golang의 표준 라이브러리 syscall은 이 메커니즘을 구현합니다.

추가로 확장하고 추상화하기 위해 github.com/fsnotify/fsnotify 패키지는 채널 기반의 크로스 플랫폼 실시간 모니터링 인터페이스를 구현합니다.

fsnotify 도구 사용

1. 필요한 패키지를 다운로드하세요

go get github.com/fsnotify/fsnotify

2. fsnotify를 사용하여 파일을 모니터링하세요

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ㅋㅋㅋ 1

52

53

54

55

56

57

58

59

60

package main;

가져오기(

"github.com/fsnotify/fsnotify" code><p class="line number54 index53 alt1"></p> <code class="js space"> "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 {};

}

테스트 결과는 다음과 같습니다.

golang에서 파일 모니터링을 구현하는 방법

tmp 디렉터리의 모든 작업이 캡처되었지만 fsnotify에 문제가 있습니다. 이는 하위 디렉터리와 하위 디렉터리의 작업 이벤트를 재귀적으로 캡처하는 데 도움이 되지 않습니다. 스스로 구현했습니다.

또 다른 문제는 폴더 이름을 수정할 때 fsnotify의 event.Name이 여전히 원래 파일 이름이므로 먼저 이전 모니터링을 제거한 다음 이름 바꾸기 이벤트에 새 모니터링을 추가해야 한다는 것입니다.

다음과 같이 수정됨:

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# 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

7 6

77

78

79

80

81

82

83

84

85

86

87

88

패키지 메인;

가져오기(package main;

  

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

}

테스트 결과는 다음과 같습니다.

golang에서 파일 모니터링을 구현하는 방법

위 예제 이후 fsnotify를 사용하여 모니터링 구성 파일을 작성하면 구성 파일이 수정되면 서비스가 다시 시작됩니다.

먼저 실행할 수 있는 exe 프로그램을 작성합니다.

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

)

 

🎜가져오기( 🎜 🎜 "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

모니터링 파일 fsnotify3.go 코드는 다음과 같습니다.

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

    }

  

가져오기(🎜

    "github.com/fsnotify/fsnotify"🎜

     "log"🎜

     "fmt"🎜

    "os/exec" 🎜

    "regexp"🎜

    "strconv"🎜

    "바이트"🎜

    "오류"🎜

    "os "🎜

    "path/filepath"🎜)🎜

  🎜

const (🎜

    confFilePath = "./conf";🎜)🎜

  🎜

//获取进程ID🎜

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

     //통해 wmic 프로세스 이름, processid 가져오기 | findstr server.exe获取进程ID🎜

    buf := 바이트. 버퍼{};🎜

    cmd := exec.Command("wmic", "프로세스", "get", "name,processid");🎜

    cmd.Stdout = &buf;🎜

    cmd.Run();🎜

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

    cmd2.Stdin = &buf;🎜

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

    if len(data) == 0 {🎜

        반환 -1, 오류.새( "찾을 수 없음");🎜

    }🎜

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

}

fsnotify3.go 파일을 실행하여 구성 파일을 모니터링합니다

golang에서 파일 모니터링을 구현하는 방법

위 그림에서 볼 수 있듯이 구성 파일에서 포트 번호를 수정하면 프로세스는 다음과 같습니다. 먼저 kill 하고 다른 프로세스를 시작합니다.

golang에서 파일 모니터링을 구현하는 방법

추천 학습: Golang 튜토리얼

위 내용은 golang에서 파일 모니터링을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.