Home>Article>Backend Development> How to implement Golang's memcache simply
The following tutorial column ofgolangwill introduce you to the simple memcache implementation method in Golang. I hope it will be helpful to friends in need!
#In the past two days while working on the project, I encountered a problem scenario of accessing global variables: write a method to obtain the token value corresponding to the id , the token needs to be cached (global variable memory cache). If it cannot be obtained or the token time expires, then send an http request to other end to get it, then cache it, and then return, then the code is as follows:
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 }
Then here comes the problem:
1. Since the gAccId2Token variable is a global variable, simultaneous reading and writing may occur. Inconsistency between reading and writing.
2. In this example, to obtain the token with id=2, and the cached token has expired, an http request will be sent to obtain it, and then the cache will be written. Assuming that the cache writing time is very long, During this period, there happens to be a large number of requests to obtain the token with id=2. Since the tokens have expired, there will be a problem of a large number of requests to the http server. Not only does it not achieve the purpose of obtaining the cache, but it also increases the size of the cache. There is pressure on the end, and there are multiple write cache operations at the same time, and Golang's map should not be atomic, so writing a large amount of memory may also cause crash problems.
Therefore, we need to lock the read and write operations:
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 }
A few notes:
1. For write operations Once the global lock is locked, other locks cannot be locked until the lock is released Unlock(), which means that the atomicity of the write operation is guaranteed.
2. If a read lock is set for the read operation, multiple threads can Rlock() the lock on an area to ensure that the area is readable until all read locks are RUnlock(). Can write lock.
3. Define the key and value types of map as interface{} type. Interface{} can receive any type, just like Object in Java.
4.Interface{} type conversion method, value.(type), that is, convert value into type type, for example: value.(int), value.(map[string]interface{}), etc. .
The above is the detailed content of How to implement Golang's memcache simply. For more information, please follow other related articles on the PHP Chinese website!