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

时间:2017-07-17 12:50:47

标签: 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有任何更改,我希望它们能够立即持久保存给用户而无需重新启动可执行文件。

2 个答案:

答案 0 :(得分:2)

使用RWMutex不会保护您免受其他程序修改的文件的侵害。这里最好的选择是在启动时将文件加载到[]byte,并在使用"bytes".Buffer时实例化goquery.NewDocumentFromReader。为了将更改传播给用户,您可以使用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

答案 1 :(得分:1)

如果要修改文件,则需要互斥锁。 RWMutex应该可以正常工作。看起来你只是在阅读它,在这种情况下你不应该看到任何锁定行为或腐败。

第二次从同一个文件句柄中读取时没有获得任何数据的原因是,当您第二次从它开始读取时,您已经在文件的末尾。如果您想再次阅读内容,则需要seek返回偏移0