fsnotify là một thư viện thông báo hệ thống tệp được viết bằng Go, có thể theo dõi sự thay đổi của các tệp và thư mục trong hệ thống tệp và thông báo cho ứng dụng khi có sự thay đổi xảy ra. Nó hỗ trợ hoạt động đa nền tảng và có thể chạy trên các hệ điều hành khác nhau như Linux, macOS, và Windows. fsnotify sử dụng cơ chế thông báo hệ thống tệp của các hệ điều hành khác nhau, như inotify trên Linux, FSEvents trên macOS, và ReadDirectoryChangesW trên Windows.

Yêu cầu việc sử dụng Go 1.17 hoặc các phiên bản sau; tài liệu đầy đủ có thể được tìm thấy tại https://pkg.go.dev/github.com/fsnotify/fsnotify

Hỗ trợ cho các hệ điều hành khác nhau:

Backend Hệ điều hành Trạng thái
inotify Linux Được hỗ trợ
kqueue BSD, macOS Được hỗ trợ
ReadDirectoryChangesW Windows Được hỗ trợ
FEN illumos Được hỗ trợ
fanotify Linux 5.9+ Chưa được hỗ trợ
AHAFS AIX Nhánh AIX; Tính năng thử nghiệm do thiếu người duy trì và môi trường kiểm thử
FSEvents macOS Yêu cầu hỗ trợ x/sys/unix
USN Journals Windows Yêu cầu hỗ trợ x/sys/windows
Polling Tất cả Chưa được hỗ trợ

Android và Solaris, mà có thể bao gồm Linux và illumos, chưa được kiểm tra.

Các Trường Hợp Sử Dụng

Các trường hợp sử dụng của fsnotify bao gồm nhưng không giới hạn trong các tình huống sau:

  1. Đồng bộ tệp thời gian thực: fsnotify có thể theo dõi sự thay đổi trong hệ thống tệp thời gian thực, phù hợp để triển khai đồng bộ tệp thời gian thực. Khi có sự thay đổi trong tệp nguồn, các thay đổi có thể được đồng bộ ngay lập tức đến tệp đích, đảm bảo tính nhất quán của các tệp.
  2. Xây dựng tự động: fsnotify có thể theo dõi sự thay đổi trong mã nguồn dự án và các tệp phụ thuộc, kích hoạt các lệnh xây dựng khi có sự thay đổi, từ đó đạt được xây dựng tự động. Điều này có thể tiết kiệm thời gian và công sức so với việc xây dựng thủ công và cải thiện hiệu suất phát triển.
  3. Sao lưu tệp: fsnotify có thể theo dõi sự thay đổi trong các tệp hoặc thư mục cần được sao lưu và khởi chạy sao lưu ngay lập tức khi có sự thay đổi. Điều này đảm bảo an toàn dữ liệu và ngăn chặn mất dữ liệu do mất tệp hoặc hỏng hóc.
  4. Giám sát nhật ký thời gian thực: fsnotify có thể theo dõi các hoạt động như tạo, sửa đổi và xóa các tệp nhật ký, kích hoạt chương trình giám sát nhật ký khi có sự thay đổi trong các tệp nhật ký, hiệu quả giám sát các thay đổi trong nội dung nhật ký thời gian thực.
  5. Giám sát bảo mật hệ thống tệp: fsnotify có thể theo dõi các sự kiện bảo mật trong hệ thống tệp, như truy cập tệp, sửa đổi và xóa. Điều này cho phép giám sát bảo mật của hệ thống tệp, sớm xác định và ghi lại các sự kiện không an toàn.

Ví Dụ Sử Dụng

Một ví dụ cơ bản:

package main

import (
    "log"

    "github.com/fsnotify/fsnotify"
)

func main() {
    // Tạo một watcher mới.
    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer watcher.Close()

    // Bắt đầu lắng nghe các sự kiện.
    go func() {
        for {
            select {
            case event, ok := <-watcher.Events:
                if !ok {
                    return
                }
                log.Println("Sự kiện:", event)
                if event.Has(fsnotify.Write) {
                    log.Println("Tệp đã sửa đổi:", event.Name)
                }
            case err, ok := <-watcher.Errors:
                if !ok {
                    return
                }
                log.Println("Lỗi:", err)
            }
        }
    }()

    // Thêm đường dẫn cần được theo dõi.
    err = watcher.Add("/tmp")
    if err != nil {
        log.Fatal(err)
    }

    // Chặn goroutine chính.
    <-make(chan struct{})
}

Các ví dụ khác có thể được tìm thấy trong cmd/fsnotify và có thể chạy bằng lệnh sau:

% go run ./cmd/fsnotify

Tài liệu chi tiết hơn có thể được tìm thấy trong godoc: https://pkg.go.dev/github.com/fsnotify/fsnotify

Câu Hỏi Thường Gặp

Liệu nó sẽ vẫn được theo dõi nếu tệp được di chuyển sang thư mục khác?

Không, trừ khi bạn đang theo dõi vị trí nó đã được di chuyển đến.

Liệu nó có theo dõi thư mục con không?

Không, bạn phải thêm theo dõi cho mỗi thư mục bạn muốn theo dõi (theo dõi đệ quy đang được dự định: #18).

Tôi có cần phải theo dõi cả kênh lỗi và sự kiện trong một goroutine cùng một lúc không?

Có. Bạn có thể sử dụng select để đọc từ cả hai kênh trong cùng một goroutine (bạn không cần phải bắt đầu một goroutine riêng biệt cho mỗi kênh; xem ví dụ).

Tại sao thông báo không hoạt động trên NFS, SMB, FUSE, /proc, hoặc /sys?

fsnotify yêu cầu sự hỗ trợ từ hệ điều hành cơ bản để hoạt động. Các giao thức hiện tại NFS và SMB không cung cấp hỗ trợ mạng cho thông báo tập tin, và các hệ thống tập tin ảo /proc và /sys cũng không cung cấp hỗ trợ.

Điều này có thể được sửa bằng cách sử dụng một người xem theo dõi bỏ phiếu (#9), nhưng nó vẫn chưa được triển khai.

Tại sao tôi nhận được nhiều sự kiện Chmod?

Một số chương trình có thể tạo ra một số lượng lớn thay đổi thuộc tính, như Spotlight trên macOS, các chương trình diệt virus, ứng dụng sao lưu, và một số ứng dụng đã biết khác. Nhìn chung, việc bỏ qua các sự kiện Chmod thường là quy tắc tốt nhất vì chúng thường không hữu ích và có thể gây ra vấn đề.

Chỉ mục Spotlight trên macOS có thể gây ra nhiều sự kiện (xem #15). Một giải pháp tạm thời là thêm các thư mục của bạn vào Cài Đặt Quyền Riêng Tư Spotlight cho đến khi chúng tôi có một triển khai FSEvents nguyên bản (xem #11).

Theo dõi tập tin không hoạt động tốt

Thường không được khuyến nghị để theo dõi từng tập tin riêng lẻ (thay vì thư mục) vì nhiều chương trình, đặc biệt là các trình soạn thảo, sẽ cập nhật tập tin theo cách nguyên tử: chúng sẽ ghi vào một tập tin tạm thời và sau đó di chuyển nó đến vị trí đích, ghi đè lên tập tin ban đầu (hoặc một biến thể của nó). Việc theo dõi trên tập tin ban đầu giờ đây đã bị mất vì tập tin không còn tồn tại.

Kết quả, một cúp điện hoặc một sự cố sẽ không dẫn đến một tập tin viết được một nửa.

Theo dõi thư mục cha và sử dụng Event.Name để lọc ra các tập tin bạn không quan tâm. Có một ví dụ trong cmd/fsnotify/file.go.

Ghi chú cho Các Nền Tảng Cụ thể

Linux

Khi một tập tin bị xóa, sự kiện REMOVE sẽ không được phát ra cho đến khi tất cả các file descriptor đã được đóng; lúc này, một sự kiện CHMOD sẽ được phát ra:

fp := os.Open("file")
os.Remove("file")        // CHMOD
fp.Close()               // REMOVE

Đây là sự kiện được gửi bởi inotify, vì vậy không nên có quá nhiều thay đổi được thực hiện với nó.

Biến sysctl fs.inotify.max_user_watches chỉ định số lượng tối đa các theo dõi cho mỗi người dùng, và fs.inotify.max_user_instances chỉ định số lượng tối đa các trường hợp inotify cho mỗi người dùng. Mỗi Watcher bạn tạo đều là một "trường hợp," và mỗi đường dẫn bạn thêm đều là một "theo dõi".

Chúng cũng có thể được tìm thấy trong /proc, với các đường dẫn /proc/sys/fs/inotify/max_user_watches/proc/sys/fs/inotify/max_user_instances.

Để tăng chúng, bạn có thể sử dụng sysctl hoặc viết các giá trị vào tập tin proc:

sysctl fs.inotify.max_user_watches=124983
sysctl fs.inotify.max_user_instances=128

Để khiến các thay đổi có hiệu lực sau khi khởi động lại, chỉnh sửa /etc/sysctl.conf hoặc /usr/lib/sysctl.d/50-default.conf (các chi tiết có thể thay đổi cho mỗi bản phân phối Linux, vui lòng tham khảo tài liệu của bản phân phối của bạn):

fs.inotify.max_user_watches=124983
fs.inotify.max_user_instances=128

Đạt đến giới hạn sẽ dẫn đến lỗi "No space left on device" hoặc "Too many open files".

kqueue (macOS, tất cả các hệ thống BSD)

kqueue yêu cầu mở một file descriptor cho mỗi tập tin được quan sát; do đó, nếu bạn đang quan sát một thư mục chứa năm tập tin, đó là sáu file descriptor. Trên những nền tảng này, bạn sẽ đạt đến giới hạn "tối đa file mở" của hệ thống nhanh hơn.

Bạn có thể sử dụng các biến sysctl kern.maxfileskern.maxfilesperproc để điều khiển số lượng tối đa các file mở.