我有一个函数,将在每个 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时实例化
。为了将更改传播给用户,您可以使用 fsnotify[1] 检测文件更改,并在必要时更新缓存文件 ("bytes".Buffer
[]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()
}
关于Golang 从文件中读取 - 锁定是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45144788/