首頁 >後端開發 >Golang >Golang的memcache如何簡單實現

Golang的memcache如何簡單實現

藏色散人
藏色散人轉載
2021-02-10 09:31:472829瀏覽

下面由golang教學欄位來介紹Golang簡單的memcache實作方法,希望對需要的朋友有幫助!

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中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除