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

从文件读取Golang-是否可以安全锁定?,go,Go,我有一个函数,每个HTTP GET请求都会调用它。该函数读取一个文件,对该文件的内容执行一些操作,然后返回这些内容的字节片。然后将该字节片作为响应体写入HTTP响应编写器 我是否需要使用互斥锁来执行此函数中的任何步骤,以防止在多个HTTP请求试图读取同一文件时锁定?如果是这样的话,一个简单的RWMutex锁定文件的读取就足够了,因为我不是在向它写东西,而是在创建它的内容的副本 以下是函数: // prepareIndex will grab index.html and add a nonce

我有一个函数,每个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
应该可以正常工作。看起来您只是在阅读它,在这种情况下,您不应该看到任何锁定行为或损坏


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

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


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

使用
RWMutex
无法保护您免受其他程序修改的文件的影响。这里最好的选择是在启动时将文件加载到
[]字节中,并在使用
goquery.NewDocumentFromReader
时实例化
“bytes.Buffer
。为了将更改传播给用户,您可以使用fsnotify[1]检测对文件的更改,并在必要时更新缓存文件(
[]字节
)(您需要
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]

使用
RWMutex
无法保护您免受其他程序修改的文件。这里最好的选择是在启动时将文件加载到
[]字节中,并在使用
goquery.NewDocumentFromReader
时实例化
“bytes.Buffer
。为了将更改传播给用户,您可以使用fsnotify[1]检测对文件的更改,并在必要时更新缓存文件(
[]字节
)(您需要
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]

啊,我明白了,谢谢你的解释!现在有道理了。。。至于第一部分,是的,我没有在阅读文件后对其进行任何修改。制作内容的克隆,该克隆将被修改(当然,这是安全的,不会被锁定)。读取后是否需要关闭文件?请注意,如果多个goroutine同时读取/查找文件,则查找方法将是一个问题。我建议在启动时将文件加载到
[]字节中,因为这样可以提高性能,并且不必担心文件在磁盘上的更改。此外,使用互斥锁锁定文件只会保护您免受自己的程序修改的文件。如果另一个程序修改该文件,您仍然会遇到问题。啊,我现在明白了,谢谢您的解释!现在有道理了。。。至于第一部分,是的,我没有在阅读文件后对其进行任何修改。制作内容的克隆,该克隆将被修改(当然,这是安全的,不会被锁定)。读取后是否需要关闭文件?请注意,如果多个goroutine同时读取/查找文件,则查找方法将是一个问题。我建议在启动时将文件加载到
[]字节中,因为这样可以提高性能,并且不必担心文件在磁盘上的更改。此外,使用互斥锁锁定文件只会保护您免受自己的程序修改的文件。如果另一个程序修改该文件,您仍然会遇到问题。回答得好!解释得很好。今天晚些时候,我将对此进行测试,并接受这些概念是否适用于我。回答不错!解释得很好。今天晚些时候,我将对此进行测试,并接受这些概念是否适用于我。您是否检查了该回购协议?您是否检查了该回购协议?