一、下载我们需要的包

  1. > go get github.com/fsnotify/fsnotify

二、使用fsnotify监控文件

  1. package main;
  2.  
  3. import (
  4. "github.com/fsnotify/fsnotify"
  5. "log"
  6. "fmt"
  7. )
  8.  
  9. func main() {
  10. //创建一个监控对象
  11. watch, err := fsnotify.NewWatcher();
  12. if err != nil {
  13. log.Fatal(err);
  14. }
  15. defer watch.Close();
  16. //添加要监控的对象,文件或文件夹
  17. err = watch.Add("./tmp");
  18. if err != nil {
  19. log.Fatal(err);
  20. }
  21. //我们另启一个goroutine来处理监控对象的事件
  22. go func() {
  23. for {
  24. select {
  25. case ev := <-watch.Events:
  26. {
  27. //判断事件发生的类型,如下5种
  28. // Create 创建
  29. // Write 写入
  30. // Remove 删除
  31. // Rename 重命名
  32. // Chmod 修改权限
  33. if ev.Op&fsnotify.Create == fsnotify.Create {
  34. log.Println("创建文件 : ", ev.Name);
  35. }
  36. if ev.Op&fsnotify.Write == fsnotify.Write {
  37. log.Println("写入文件 : ", ev.Name);
  38. }
  39. if ev.Op&fsnotify.Remove == fsnotify.Remove {
  40. log.Println("删除文件 : ", ev.Name);
  41. }
  42. if ev.Op&fsnotify.Rename == fsnotify.Rename {
  43. log.Println("重命名文件 : ", ev.Name);
  44. }
  45. if ev.Op&fsnotify.Chmod == fsnotify.Chmod {
  46. log.Println("修改权限 : ", ev.Name);
  47. }
  48. }
  49. case err := <-watch.Errors:
  50. {
  51. log.Println("error : ", err);
  52. return;
  53. }
  54. }
  55. }
  56. }();
  57.  
  58. //循环
  59. select {};
  60. }

测试结果如下:

我们在tmp目录下的操作都被捕捉到了,但是fsnotify有一个问题,它无法递归的帮我们捕捉子目录、孙子目录的操作事件,这需要我们自已来实现。

还有一个问题就是当们修改文件夹名称时,fsnotify中event.Name仍然是原来的文件名,这就需要我们在重命名事件中,先移除之前的监控,然后添加新的监控。

修改如下:

  1. package main;
  2.  
  3. import (
  4. "github.com/fsnotify/fsnotify"
  5. "fmt"
  6. "path/filepath"
  7. "os"
  8. )
  9.  
  10. type Watch struct {
  11. watch *fsnotify.Watcher;
  12. }
  13.  
  14. //监控目录
  15. func (w *Watch) watchDir(dir string) {
  16. //通过Walk来遍历目录下的所有子目录
  17. filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
  18. //这里判断是否为目录,只需监控目录即可
  19. //目录下的文件也在监控范围内,不需要我们一个一个加
  20. if info.IsDir() {
  21. path, err := filepath.Abs(path);
  22. if err != nil {
  23. return err;
  24. }
  25. err = w.watch.Add(path);
  26. if err != nil {
  27. return err;
  28. }
  29. fmt.Println("监控 : ", path);
  30. }
  31. return nil;
  32. });
  33. go func() {
  34. for {
  35. select {
  36. case ev := <-w.watch.Events:
  37. {
  38. if ev.Op&fsnotify.Create == fsnotify.Create {
  39. fmt.Println("创建文件 : ", ev.Name);
  40. //这里获取新创建文件的信息,如果是目录,则加入监控中
  41. fi, err := os.Stat(ev.Name);
  42. if err == nil && fi.IsDir() {
  43. w.watch.Add(ev.Name);
  44. fmt.Println("添加监控 : ", ev.Name);
  45. }
  46. }
  47. if ev.Op&fsnotify.Write == fsnotify.Write {
  48. fmt.Println("写入文件 : ", ev.Name);
  49. }
  50. if ev.Op&fsnotify.Remove == fsnotify.Remove {
  51. fmt.Println("删除文件 : ", ev.Name);
  52. //如果删除文件是目录,则移除监控
  53. fi, err := os.Stat(ev.Name);
  54. if err == nil && fi.IsDir() {
  55. w.watch.Remove(ev.Name);
  56. fmt.Println("删除监控 : ", ev.Name);
  57. }
  58. }
  59. if ev.Op&fsnotify.Rename == fsnotify.Rename {
  60. fmt.Println("重命名文件 : ", ev.Name);
  61. //如果重命名文件是目录,则移除监控
  62. //注意这里无法使用os.Stat来判断是否是目录了
  63. //因为重命名后,go已经无法找到原文件来获取信息了
  64. //所以这里就简单粗爆的直接remove好了
  65. w.watch.Remove(ev.Name);
  66. }
  67. if ev.Op&fsnotify.Chmod == fsnotify.Chmod {
  68. fmt.Println("修改权限 : ", ev.Name);
  69. }
  70. }
  71. case err := <-w.watch.Errors:
  72. {
  73. fmt.Println("error : ", err);
  74. return;
  75. }
  76. }
  77. }
  78. }();
  79. }
  80.  
  81. func main() {
  82. watch, _ := fsnotify.NewWatcher()
  83. w := Watch{
  84. watch: watch,
  85. }
  86. w.watchDir("./tmp");
  87. select {};
  88. }

测试结果如下:

经过上面的例子,我们通过fsnotify来写一个监控配置文件,如果配置文件有修改,就重新启动服务。

我们先写一个可以运行的exe程序,server.go代码如下:

  1. package main;
  2.  
  3. import (
  4. "io/ioutil"
  5. "log"
  6. "encoding/json"
  7. "net"
  8. "fmt"
  9. "os"
  10. "os/signal"
  11. )
  12.  
  13. const (
  14. confFilePath = "./conf/conf.json";
  15. )
  16.  
  17. //我们这里只是演示,配置项只设置一个
  18. type Conf struct {
  19. Port int `json:port`;
  20. }
  21.  
  22. func main() {
  23. //读取文件内容
  24. data, err := ioutil.ReadFile(confFilePath);
  25. if err != nil {
  26. log.Fatal(err);
  27. }
  28. var c Conf;
  29. //解析配置文件
  30. err = json.Unmarshal(data, &c);
  31. if err != nil {
  32. log.Fatal(err);
  33. }
  34. //根据配置项来监听端口
  35. lis, err := net.Listen("tcp", fmt.Sprintf(":%d", c.Port));
  36. if err != nil {
  37. log.Fatal(err);
  38. }
  39. log.Println("server start");
  40. go func() {
  41. ch := make(chan os.Signal);
  42. //获取程序退出信号
  43. signal.Notify(ch, os.Interrupt, os.Kill);
  44. <-ch;
  45. log.Println("server exit");
  46. os.Exit(1);
  47. }();
  48. for {
  49. conn, err := lis.Accept();
  50. if err != nil {
  51. continue;
  52. }
  53. go func(conn net.Conn) {
  54. defer conn.Close();
  55. conn.Write([]byte("hello\n"));
  56. }(conn);
  57. }
  58. }

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

  1. > go build server.go

监控文件fsnotify3.go代码如下:

  1. package main;
  2.  
  3. import (
  4. "github.com/fsnotify/fsnotify"
  5. "log"
  6. "fmt"
  7. "os/exec"
  8. "regexp"
  9. "strconv"
  10. "bytes"
  11. "errors"
  12. "os"
  13. "path/filepath"
  14. )
  15.  
  16. const (
  17. confFilePath = "./conf";
  18. )
  19.  
  20. //获取进程ID
  21. func getPid(processName string) (int, error) {
  22. //通过wmic process get name,processid | findstr server.exe获取进程ID
  23. buf := bytes.Buffer{};
  24. cmd := exec.Command("wmic", "process", "get", "name,processid");
  25. cmd.Stdout = &buf;
  26. cmd.Run();
  27. cmd2 := exec.Command("findstr", processName);
  28. cmd2.Stdin = &buf;
  29. data, _ := cmd2.CombinedOutput();
  30. if len(data) == 0 {
  31. return -1, errors.New("not find");
  32. }
  33. info := string(data);
  34. //这里通过正则把进程id提取出来
  35. reg := regexp.MustCompile(`[0-9]+`);
  36. pid := reg.FindString(info);
  37. return strconv.Atoi(pid);
  38. }
  39.  
  40. //启动进程
  41. func startProcess(exePath string, args []string) error {
  42. attr := &os.ProcAttr{
  43. //files指定新进程继承的活动文件对象
  44. //前三个分别为,标准输入、标准输出、标准错误输出
  45. Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
  46. //新进程的环境变量
  47. Env: os.Environ(),
  48. }
  49.  
  50. p, err := os.StartProcess(exePath, args, attr);
  51. if err != nil {
  52. return err;
  53. }
  54. fmt.Println(exePath, "进程启动");
  55. p.Wait();
  56. return nil;
  57. }
  58.  
  59. func main() {
  60. //创建一个监控对象
  61. watch, err := fsnotify.NewWatcher();
  62. if err != nil {
  63. log.Fatal(err);
  64. }
  65. defer watch.Close();
  66. //添加要监控的文件
  67. err = watch.Add(confFilePath);
  68. if err != nil {
  69. log.Fatal(err);
  70. }
  71. //我们另启一个goroutine来处理监控对象的事件
  72. go func() {
  73. for {
  74. select {
  75. case ev := <-watch.Events:
  76. {
  77. //我们只需关心文件的修改
  78. if ev.Op&fsnotify.Write == fsnotify.Write {
  79. fmt.Println(ev.Name, "文件写入");
  80. //查找进程
  81. pid, err := getPid("server.exe");
  82. //获取运行文件的绝对路径
  83. exePath, _ := filepath.Abs("./server.exe")
  84. if err != nil {
  85. //启动进程
  86. go startProcess(exePath, []string{});
  87. } else {
  88. //找到进程,并退出
  89. process, err := os.FindProcess(pid);
  90. if err == nil {
  91. //让进程退出
  92. process.Kill();
  93. fmt.Println(exePath, "进程退出");
  94. }
  95. //启动进程
  96. go startProcess(exePath, []string{});
  97. }
  98. }
  99. }
  100. case err := <-watch.Errors:
  101. {
  102. fmt.Println("error : ", err);
  103. return;
  104. }
  105. }
  106. }
  107. }();
  108.  
  109. //循环
  110. select {};
  111. }

我们运行fsnotify3.go文件来监控我们的配置文件

通过上面的图可以看到,当我们修改配置文件中的端口号时,会先kill掉进程,然后再启动一个进程。

golang 通过fsnotify监控文件,并通过文件变化重启程序的更多相关文章

  1. 使用FileSystemWatcher监控文件夹及文件

    引言 这一周主要精力集中学习一个同事开发的本地文件搜索项目上,其中客户端添加共享文件时主要是使用FileSystemWatcher 监控文件,并在各种事件发生时向服务器发送消息. 解决方法 FileS ...

  2. Python 的 pyinotify 模块 监控文件夹和文件的变动

    官方参考: https://github.com/seb-m/pyinotify/wiki/Events-types https://github.com/seb-m/pyinotify/wiki/I ...

  3. C#监控指定目录的文件变化的代码

    如下的资料是关于C#监控指定目录的文件变化的代码. FileSystemWatcher watcher = new FileSystemWatcher();watcher.Path = @" ...

  4. C# FileSystemWatcher 在监控文件夹和文件时的用法

    概述 最近学习FileSystemWatcher的用法,它主要是监控一个文件夹,当文件夹内的文件要是有更改就要记录下来,我就整理下我对FileSystemWatcher 的理解和用法. FileSys ...

  5. watch命令的监控结果输出到文件

    watch命令是为命令行输出设计的工具,其结果包含很多不可打印的字符,所以输出重定向到文件中很不方便,比如这样做的话有很多乱码: (watch -n 60 <mycommand> ) &g ...

  6. 基于EasyHook实现监控explorer资源管理器文件复制、删除、剪切等操作

    一.前言 最近自己在研究一个项目,需要实现对explorer资源管理器文件操作的监控功能,网上找到一些通过C++实现Hook explorer文件操作的方法,由于本人习惯用.NET开发程序,加之C/C ...

  7. vc++ 监控指定路径下文件变化

    参考MSDN文档 https://docs.microsoft.com/zh-cn/windows/desktop/api/winbase/nf-winbase-readdirectorychange ...

  8. Java实现文件夹下文件实时监控

    一.commons-io方法 1.使用Commons-io的monitor下的相关类可以处理对文件进行监控,它采用的是观察者模式来实现的 (1)可以监控文件夹的创建.删除和修改 (2)可以监控文件的创 ...

  9. [Golang学习笔记] 03 库源码文件

    库源码文件:不能被直接运行的源码文件,它仅用于存放程序实体,这些程序实体可以被其他代码使用. 代码包声明的基本规则: 1. 同目录下的源码文件的代码包声明语句要一致.也就是说,它们要同属于一个代码包( ...

随机推荐

  1. Spring事务异常rollback-only

    转自:https://blog.csdn.net/sgls652709/article/details/49472719 前言 在利用单元测试验证spring事务传播机制的时候出现了下面的异常: Tr ...

  2. Letter S Pronounced [z]

    Letter S Pronounced [z] Share Tweet Share Since English is not a phonetic language, one letter is no ...

  3. How to Pronounce TH after N or Z

    How to Pronounce TH after N or Z Share Tweet Share Tagged With: Linking Consonant to Consonant The T ...

  4. 高并发架构技术|缓存失效、缓存穿透问题 PHP 代码解决

    问题描述 缓存失效: 引起这个原因的主要因素是高并发下,我们一般设定一个缓存的过期时间时,可能有一些会设置5分钟啊,10分钟这些:并发很高时可能会出在某一个时间同时生成了很多的缓存,并且过期时间在同一 ...

  5. 【384】reduce归纳、map映射、filter筛选 的用法

    参考:4. Map, Filter and Reduce — Python Tips 0.1 documentation 参考:Python的functools.reduce用法 Map:映射,对于列 ...

  6. SQL Server 中BIT类型字段增删查改那点事

    话说BIT类型字段之前,先看“诡异”的一幕,执行Update成功,但是查询出来的结果依然是1,而不是Update的2 当别人问起我来的时候,本人当时也是处于懵逼状态的,后面联想具体的业务突然想起来这个 ...

  7. Class-reference types 类引用类型--快要失传的技术

    先摘一段原版的说明: A class-reference type, sometimes called a metaclass, is denoted by a construction of the ...

  8. python递归、collections系列以及文件操作进阶

    global log 127.0.0.1 local2 daemon maxconn log 127.0.0.1 local2 info defaults log global mode http t ...

  9. oracle数据表中的中文变问号

    先查询一下select userenv('language') from dual;查看oracle字符集, $ sqlplus /nolog SQL> connect sys/oracle a ...

  10. nagiosQL访问时报错PHP message: PHP Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead

    nagiosQL安装环境: CentOS release 6.4 (Final) php-5.5.4 nagiosql_320 nginx version: nginx/1.2.3 安装一切正常,当访 ...