golang 通过fsnotify监控文件,并通过文件变化重启程序
一、下载我们需要的包
- > go get github.com/fsnotify/fsnotify
二、使用fsnotify监控文件
- package main;
- import (
- "github.com/fsnotify/fsnotify"
- "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 := <-watch.Events:
- {
- //判断事件发生的类型,如下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 := <-watch.Errors:
- {
- log.Println("error : ", err);
- return;
- }
- }
- }
- }();
- //循环
- select {};
- }
测试结果如下:
我们在tmp目录下的操作都被捕捉到了,但是fsnotify有一个问题,它无法递归的帮我们捕捉子目录、孙子目录的操作事件,这需要我们自已来实现。
还有一个问题就是当们修改文件夹名称时,fsnotify中event.Name仍然是原来的文件名,这就需要我们在重命名事件中,先移除之前的监控,然后添加新的监控。
修改如下:
- 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 := <-w.watch.Events:
- {
- 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 := <-w.watch.Errors:
- {
- fmt.Println("error : ", err);
- return;
- }
- }
- }
- }();
- }
- func main() {
- watch, _ := fsnotify.NewWatcher()
- w := Watch{
- watch: watch,
- }
- w.watchDir("./tmp");
- select {};
- }
测试结果如下:
经过上面的例子,我们通过fsnotify来写一个监控配置文件,如果配置文件有修改,就重新启动服务。
我们先写一个可以运行的exe程序,server.go代码如下:
- package main;
- import (
- "io/ioutil"
- "log"
- "encoding/json"
- "net"
- "fmt"
- "os"
- "os/signal"
- )
- const (
- confFilePath = "./conf/conf.json";
- )
- //我们这里只是演示,配置项只设置一个
- 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);
- <-ch;
- 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("hello\n"));
- }(conn);
- }
- }
使用如下命令,编译成exe文件
- > go build server.go
监控文件fsnotify3.go代码如下:
- 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");
- }
- 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 := <-watch.Events:
- {
- //我们只需关心文件的修改
- 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 := <-watch.Errors:
- {
- fmt.Println("error : ", err);
- return;
- }
- }
- }
- }();
- //循环
- select {};
- }
我们运行fsnotify3.go文件来监控我们的配置文件
通过上面的图可以看到,当我们修改配置文件中的端口号时,会先kill掉进程,然后再启动一个进程。
golang 通过fsnotify监控文件,并通过文件变化重启程序的更多相关文章
- 使用FileSystemWatcher监控文件夹及文件
引言 这一周主要精力集中学习一个同事开发的本地文件搜索项目上,其中客户端添加共享文件时主要是使用FileSystemWatcher 监控文件,并在各种事件发生时向服务器发送消息. 解决方法 FileS ...
- Python 的 pyinotify 模块 监控文件夹和文件的变动
官方参考: https://github.com/seb-m/pyinotify/wiki/Events-types https://github.com/seb-m/pyinotify/wiki/I ...
- C#监控指定目录的文件变化的代码
如下的资料是关于C#监控指定目录的文件变化的代码. FileSystemWatcher watcher = new FileSystemWatcher();watcher.Path = @" ...
- C# FileSystemWatcher 在监控文件夹和文件时的用法
概述 最近学习FileSystemWatcher的用法,它主要是监控一个文件夹,当文件夹内的文件要是有更改就要记录下来,我就整理下我对FileSystemWatcher 的理解和用法. FileSys ...
- watch命令的监控结果输出到文件
watch命令是为命令行输出设计的工具,其结果包含很多不可打印的字符,所以输出重定向到文件中很不方便,比如这样做的话有很多乱码: (watch -n 60 <mycommand> ) &g ...
- 基于EasyHook实现监控explorer资源管理器文件复制、删除、剪切等操作
一.前言 最近自己在研究一个项目,需要实现对explorer资源管理器文件操作的监控功能,网上找到一些通过C++实现Hook explorer文件操作的方法,由于本人习惯用.NET开发程序,加之C/C ...
- vc++ 监控指定路径下文件变化
参考MSDN文档 https://docs.microsoft.com/zh-cn/windows/desktop/api/winbase/nf-winbase-readdirectorychange ...
- Java实现文件夹下文件实时监控
一.commons-io方法 1.使用Commons-io的monitor下的相关类可以处理对文件进行监控,它采用的是观察者模式来实现的 (1)可以监控文件夹的创建.删除和修改 (2)可以监控文件的创 ...
- [Golang学习笔记] 03 库源码文件
库源码文件:不能被直接运行的源码文件,它仅用于存放程序实体,这些程序实体可以被其他代码使用. 代码包声明的基本规则: 1. 同目录下的源码文件的代码包声明语句要一致.也就是说,它们要同属于一个代码包( ...
随机推荐
- Spring事务异常rollback-only
转自:https://blog.csdn.net/sgls652709/article/details/49472719 前言 在利用单元测试验证spring事务传播机制的时候出现了下面的异常: Tr ...
- Letter S Pronounced [z]
Letter S Pronounced [z] Share Tweet Share Since English is not a phonetic language, one letter is no ...
- 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 ...
- 高并发架构技术|缓存失效、缓存穿透问题 PHP 代码解决
问题描述 缓存失效: 引起这个原因的主要因素是高并发下,我们一般设定一个缓存的过期时间时,可能有一些会设置5分钟啊,10分钟这些:并发很高时可能会出在某一个时间同时生成了很多的缓存,并且过期时间在同一 ...
- 【384】reduce归纳、map映射、filter筛选 的用法
参考:4. Map, Filter and Reduce — Python Tips 0.1 documentation 参考:Python的functools.reduce用法 Map:映射,对于列 ...
- SQL Server 中BIT类型字段增删查改那点事
话说BIT类型字段之前,先看“诡异”的一幕,执行Update成功,但是查询出来的结果依然是1,而不是Update的2 当别人问起我来的时候,本人当时也是处于懵逼状态的,后面联想具体的业务突然想起来这个 ...
- Class-reference types 类引用类型--快要失传的技术
先摘一段原版的说明: A class-reference type, sometimes called a metaclass, is denoted by a construction of the ...
- python递归、collections系列以及文件操作进阶
global log 127.0.0.1 local2 daemon maxconn log 127.0.0.1 local2 info defaults log global mode http t ...
- oracle数据表中的中文变问号
先查询一下select userenv('language') from dual;查看oracle字符集, $ sqlplus /nolog SQL> connect sys/oracle a ...
- 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 安装一切正常,当访 ...