前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅析gowatch监听文件变动实现原理

浅析gowatch监听文件变动实现原理

作者头像
Go学堂
发布2023-01-31 15:11:16
1.4K0
发布2023-01-31 15:11:16
举报
文章被收录于专栏:Go工具箱

刚开始接触go时,发现go程序和php程序的其中一个不同是php是解释性语言,go是编译型语言,即每次在有程序改动后,需要重新运行 go run或go build进行重新编译,更改才能生效,实则不便。于是乎在网络上搜索发现了gowatch这个包,该包可通过监听当前目录下相关文件的变动,对go文件实时编译,提高研发效率。那gowatch又是如何做到监听文件变化的呢?

通过阅读源码我们发现,在linux内核中,有一种用于通知用户空间程序文件系统变化的机制—Inotify。它监控文件系统,并且及时向专门的应用程序发出相关的事件警告,比如删除、读、写和卸载操作等。您还可以跟踪活动的源头和目标等细节。Golang的标准库syscall实现了该机制。为进一步扩展,实现了fsnotify包实现了一个基于通道的、跨平台的实时监听接口。如下图:

根据上图可知,监听文件的变化主要依赖于linux内核的INotify接口机制。Go的标准库中对其做了实现。而fsnotify package的主要作用就是将进一步封装成watcher结构体和事件类型结构体的封装,从而实现事件的判断以及目录的监听。下面看下 fsnotify package中对watcher的封装。

代码语言:javascript
复制
type Watcher struct {

    mu sync.Mutex // Map access

    fd int // File descriptor (as returned by the inotify_init() syscall)

    watches map[string]*watch // Map of inotify watches (key: path)

    fsnFlags map[string]uint32 // Map of watched files to flags used for filter

    fsnmut sync.Mutex // Protects access to fsnFlags.

    paths map[int]string // Map of watched paths (key: watch descriptor)

    Error chan error // Errors are sent on this channel

    internalEvent chan *FileEvent // Events are queued on this channel

    Event chan *FileEvent // Events are returned on this channel

    done chan bool // Channel for sending a "quit message" to the reader goroutine

    isClosed bool // Set to true when Close() is first called

}

linux内核Inotify接口简介

inotify中主要涉及3个接口。分别是inotify_init, inotify_add_watch,read。具体如下:

接口名

作用

int fd = inotify_init()

创建inotify实例,返回对应的文件描述符

inotify_add_watch (fd, path, mask)

注册被监视目录或文件的事件

read (fd, buf, BUF_LEN)

读取监听到的文件事件

Inotify可以监听的文件系统事件列表:

事件名称

事件说明

IN_ACCESS

文件被访问

IN_MODIFY

文件被 write

IN_CLOSE_WRITE

可写文件被 close

IN_OPEN

文件被 open

IN_MOVED_TO

文件被移来,如 mv、cp

IN_CREATE

创建新文件

IN_DELETE

文件被删除,如 rm

IN_DELETE_SELF

自删除,即一个可执行文件在执行时删除自己

IN_MOVE_SELF

自移动,即一个可执行文件在执行时移动自己

IN_ATTRIB

文件属性被修改,如 chmod、chown、touch 等

IN_CLOSE_NOWRITE

不可写文件被 close

IN_MOVED_FROM

文件被移走,如 mv

IN_UNMOUNT

宿主文件系统被 umount

IN_CLOSE

文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)

IN_MOVE

文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)

示例应用

接下来是一个简易的示例应用,具体的应用实例可参考github.com/silenceper/gowatch包源代码 。

主要逻辑如下:

  1. 初始化watcher对象
  2. 将文件或目录加入到watcher监控对象的队列
  3. 启动监听协程,实时获取文件对象事件
代码语言:javascript
复制
package main

import (

    "fmt"

    "github.com/howeyc/fsnotify"
    "runtime"
)


var exit chan bool

func main() {
    //1、初始化监控对象watcher
    watcher, err := fsnotify.NewWatcher() 

    if err != nil {

        fmt.Printf("Fail to create new Watcher[ %s ]\n", err)
    }

    //3、启动监听文件对象事件协程
    go func() {
        fmt.Println("开始监听文件变化")
        for {
            select {
            case e := <-watcher.Event:
                // 这里添加根据文件变化的业务逻辑
                fmt.Printf("监听到文件 - %s变化\n", e.Name)
                if e.IsCreate() {
                    fmt.Println("监听到文件创建事件")
                }
                if e.IsDelete() {
                    fmt.Println("监听到文件删除事件")
                }
                if e.IsModify() {
                    fmt.Println("监听到文件修改事件")
                }
                if e.IsRename() {
                    fmt.Println("监听到文件重命名事件")
                }
                if e.IsAttrib() {
                    fmt.Println("监听到文件属性修改事件")
                }

                fmt.Println("根据文件变化开始执行业务逻辑")
            
            case err := <-watcher.Error:

                fmt.Printf(" %s\n", err.Error())
            }
        }
    }()
    // 2、将需要监听的文件加入到watcher的监听队列中
    paths := []string{"config.yml"}

    for _, path := range paths {

        err = watcher.Watch(path) //将文件加入监听

        if err != nil {

            fmt.Sprintf("Fail to watch directory[ %s ]\n", err)
        }
    }

    <-exit
    runtime.Goexit()
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go学堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档