3 回答
TA貢獻1831條經驗 獲得超10個贊
這讓我想起了一個問題,有人在實施類似的事情:
我給出了一個實現這樣一個中間層的例子的答案。我認為這符合您的想法:定期跟蹤對同一資源的請求并防止它們并行重新計算。
如果您有一個單獨的例程負責接收請求和管理對緩存的訪問,則不需要顯式鎖(盡管有一個隱藏在通道中)。無論如何,我不知道你的應用程序的細節,但考慮到你需要檢查緩存(可能被鎖定)和(偶爾)執行丟失條目的昂貴計算——鎖定地圖查找對我來說似乎不是一個大問題。如果您認為這會有所幫助,您也可以始終跨越更多這樣的中間層例程,但是您需要一種確定性方式來路由請求(因此每個緩存條目都由單個例程管理)。
很抱歉沒有給您帶來靈丹妙藥的解決方案,但聽起來您無論如何都可以很好地解決問題。
TA貢獻1871條經驗 獲得超13個贊
緩存和性能問題總是很棘手,你應該總是制定一個基本的解決方案來進行基準測試,以確保你的假設是正確的。但是如果我們知道瓶頸是獲取資源并且緩存會帶來顯著的回報,那么你可以使用 Go 的通道來實現排隊。假設這response是您的資源類型。
type request struct {
back chan *response
}
func main() {
c := make(chan request,10) // non-blocking
go func(input chan request){
var cached *response
for _,i := range input {
if cached == nil { // only make request once
cached = makeLongRunningRequest()
}
i.back <- cached
}
}(c)
resp := make(chan *response)
c <- request{resp} // cache miss
c <- request{resp} // will get queued
c <- request{resp} // will get queued
for _,r := range resp {
// do something with response
}
}
這里我們只獲取一個資源,但你可以為你想要獲取的每個資源啟動一個 goroutine。Goroutines 很便宜,所以除非你需要同時緩存數百萬個資源,否則你應該沒問題。你當然也可以在一段時間后殺死你的 goroutine。
為了跟蹤哪個資源 id 屬于哪個頻道,我會使用地圖
map[resourceId]chan request
帶有互斥鎖。同樣,如果獲取資源是瓶頸,那么鎖定地圖的成本應該可以忽略不計。如果鎖定地圖被證明是一個問題,請考慮使用分片地圖。
總的來說,您似乎進展順利。我建議盡量讓你的設計盡可能簡單,并在可能的情況下使用通道而不是鎖。它們確實可以防止可怕的并發錯誤。
- 3 回答
- 0 關注
- 288 瀏覽
添加回答
舉報
