2017-07-17 48 views
0

我有一個函數會在每個HTTP GET請求中調用。該函數讀取文件,對該文件的內容執行一些操作,並返回這些內容的一部分字節。然後將這部分字節作爲響應主體寫入HTTP響應寫入器。Golang從文件中讀取 - 是否可以鎖定?

我是否需要在此函數中的任何步驟中使用互斥鎖來防止多個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) 
} 

我也想過只加載文件一次當主開始,但我有一個問題,即只在第一次請求可以看到的數據,並在後續請求什麼也沒看見。可能是我讀文件時的錯誤。但我更喜歡我目前的做法,因爲如果index.html有任何更改,我希望它們可以立即保存到用戶,而無需重新啓動可執行文件。

+1

你檢查這個回購https://github.com/nightlyone/lockfile? –

回答

1

如果你正在修改文件,你需要一個互斥鎖。 RWMutex應該可以正常工作。它看起來像你正在閱讀它,在這種情況下,你不應該看到任何鎖定行爲或腐敗。

第二次從同一個文件句柄中讀取數據時沒有得到任何數據的原因是當您第二次開始讀取文件時,您已經在文件末尾。如果要再次讀取內容,則需要seek返回偏移0

+0

啊我現在看到了,謝謝你的解釋!這是有道理的......至於第一部分,是的,在閱讀完文件後我沒有對文件做任何修改。內容的克隆被製作,並且該克隆將被修改(但是從鎖定當然是安全的)。 閱讀後需要關閉文件嗎? – Lansana

+0

請注意,如果有多個goroutine正在同時讀取/查找文件,則查找方法將成爲問題。我建議在啓動時將文件加載到一個'[]字節'中,因爲這樣可以提高性能,並且不需要擔心磁盤上的文件更改。 此外,使用互斥鎖定文件只會保護您免受自己程序修改的文件的影響。如果另一個程序修改了文件,您仍然會遇到問題。 – yazgazan

2

使用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

+0

好的答案!很好解釋。我會在今天晚些時候進行測試,並接受這些概念是否適合我。 – Lansana

相關問題