下面由golang教學欄位來介紹Golang簡單的memcache實作方法,希望對需要的朋友有幫助!
這兩天在做專案的過程中遇到了一個存取全域變數的問題場景:寫一個方法,取得id對應的token值,token需要快取起來(全域變數記憶體快取),如果取得不到或token的時間過期,那麼發送http請求到其他端去取,然後快取起來,然後再返回,那麼程式碼如下:
code.go:
package person import ( "time" ) var gAccId2Token map[int]interface{} = make(map[int]interface{}) func GetTokenByAccountId(accountId uint, acl string) (map[string]interface{}, error) { //get token from cache if token, ok := gAccId2Token[accountId]; ok { if token != nil { now := time.Now().Unix() if int(now) < int(token.(map[string]interface{})["expireDate"].(float64)) { return token.(map[string]interface{}), nil } } } token, err := getTokenByHttpUrl(apiUrl) if err != nil { return map[string]interface{}{}, err } gAccId2Token[accountId] = token return token.(map[string]interface{}), nil }
那麼問題來了:
1.由於gAccId2Token變數是全域變量,那麼會出現同時讀寫的情況,則會可能出現讀寫不一致的情況。
2.就本例來看,獲取id=2的token,而緩存的token已經過期了,那麼就會發送http請求去獲取,之後寫緩存,假設寫緩存的時間很長,而在這段時間內,又恰好有大量請求來獲取id=2的token,由於token都過期了,就會出現大量請求http服務端的問題,不僅沒有起到獲取緩存的目的,又增大了後端的壓力,同時又有多個寫快取的操作,而golang的map應該不是原子的,那麼大量寫記憶體也可能會造成crash的問題。
因此,我們需要對讀寫操作進行加鎖:
memcache.go:
package person import ( "sync" "time" ) type memoryCache struct { lock *sync.RWMutex items map[interface{}]interface{} } func (mc *memoryCache) set(key interface{}, value interface{}) error { mc.lock.Lock() defer mc.lock.Unlock() mc.items[key] = value return nil } func (mc *memoryCache) get(key interface{}) interface{} { mc.lock.RLock() defer mc.lock.RUnlock() if val, ok := mc.items[key]; ok { return val } return nil } var gAccId2Token *memoryCache = &memoryCache{ lock: new(sync.RWMutex), items: make(map[interface{}]interface{}), } func GetTokenByAccountId(accountId uint, acl string) (map[string]interface{}, error) { //get token from cache token := gAccId2Token.get(accountId) if token != nil { now := time.Now().Unix() if int(now) < int(token.(map[string]interface{})["expireDate"].(float64)) { return token.(map[string]interface{}), nil } } token, err := getTokenByHttpUrl(apiUrl) if err != nil { return map[string]interface{}{}, err } gAccId2Token.set(accountId, token) return token.(map[string]interface{}), nil }
幾點說明:
#1.為寫入操作上了全域鎖,一旦Lock()之後,其他lock就無法上鎖,直到釋放鎖Unlock()之後才行,也就是說保證寫入作業的原子性。
2.而為讀取操作上了讀鎖,那麼可以有多個線程Rlock()對一個區域枷鎖,從而保證區域是可讀的,直到所有讀鎖都RUnlock()之後,才可以上寫鎖。
3.將map的key和value的類型定義為interface{}類型,interface{}可以接收任何類型,就像是Java中的Object。
4.interface{}類型轉換的方法,value.(type),即將value轉換成為type類型,例如:value.(int),value.(map[string]interface{})等等。
以上是Golang的memcache如何簡單實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!