Golang 从文件中读取 - 锁定是否安全?

标签 go

我有一个函数,将在每个 HTTP GET 请求上调用。该函数读取一个文件,对该文件的内容做一些处理,然后返回这些内容的一部分字节。然后将那段字节作为响应主体写入 HTTP 响应编写器。

我是否需要为此函数中的任何步骤使用互斥锁,以防止在多个 HTTP 请求尝试读取同一文件时发生锁定?如果是这样,一个简单的 RWMutex 锁定文件的读取就足够了吗,因为我实际上并不是在写入它,而是在创建其内容的副本?

函数如下:

// prepareIndex will grab index.html and add a nonce to the script tags for the CSP header compliance.
func prepareIndex(nonce string) []byte {
    // Load index.html.
    file, err := os.Open("./client/dist/index.html")
    if err != nil {
        log.Fatal(err)
    }

    // Convert to goquery document.
    doc, err := goquery.NewDocumentFromReader(file)
    if err != nil {
        fmt.Println(err)
    }

    // Find all script tags and set nonce.
    doc.Find("body > script").SetAttr("nonce", nonce)

    // Grab the HTML string.
    html, err := doc.Html()
    if err != nil {
        fmt.Println(err)
    }

    return []byte(html)
}

我也考虑过在 main 启动时只加载一次文件,但我遇到了一个问题,即只有第一个请求可以看到数据,而后续请求什么也看不到。可能是我读取文件的方式有误。但我实际上更喜欢我当前的方法,因为如果 index.html 有任何更改,我希望它们立即保留给用户,而无需重新启动可执行文件。

最佳答案

使用 RWMutex 不会保护您免受文件被另一个程序修改的影响。这里最好的选择是在启动时将文件加载到 []byte 中,并在每次使用 goquery.NewDocumentFromReader"bytes".Buffer。为了将更改传播给用户,您可以使用 fsnotify[1] 检测文件更改,并在必要时更新缓存文件 ([]byte)(您需要 RWMutex 用于该操作)。

例如:

type CachedFile struct {
    sync.RWMutex
    FileName string
    Content  []byte
    watcher  *fsnotify.Watcher
}

func (c *CachedFile) Buffer() *bytes.Buffer {
    c.RLock()
    defer c.RUnlock()
    return bytes.NewBuffer(c.Content)
}

func (c *CachedFile) Load() error {
    c.Lock()
    content, err := ioutil.ReadAll(c.FileName)
    if err != nil {
        c.Unlock()
        return err
    }
    c.Content = content
    c.Unlock()
}

func (c *CachedFile) Watch() error {
    var err error

    c.watcher, err = fsnotify.NewWatcher()
    if err != nil {
        return err
    }

    go func() {
        for ev := range c.watcher.Events {
            if ev.Op != fsnotify.Write {
                continue
            }
            err := c.Load()
            if err != nil {
                log.Printf("loading %q: %s", c.FileName, err)
            }
        }
    }()

    err = c.watcher.Add(c.FileName)
    if err != nil {
        c.watcher.Close()
        return err
    }

    return nil
}

func (c *CachedFile) Close() error {
    return c.watcher.Close()
}

[1] https://godoc.org/github.com/fsnotify/fsnotify

关于Golang 从文件中读取 - 锁定是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45144788/

相关文章:

go - 使用 http.TimeoutHandler 或 ReadTimeout/WriteTimeout?

go - 在循环中清除和重写 slice

go - Float64 在 go 中键入常量,失去精度

go - 有 time.Sleep() 的无限循环与没有 time.Sleep() 的无限循环

go - 当 T2 具有 T1 的基础类型时,从 []T1 转换为 []T2

file - 如何使用 golang Rest api 将文件或图像路径 url 返回到 forntend?

go - Go中调用特定类型的函数

http - 我的 golang 网络服务器是否需要以 root 用户身份运行才能支持 HTTPS/TLS?

mysql - Gorm First或创造理解

windows - 如何在 GoSublime 的命令行界面中退出 "go run"?