工作中碰到缓存失败时,数据库的惊群,本来之前用Redis的SETNX来做锁处理,后来想想,既然用golang写了服务,当然可以把锁直接做在内存里,就自己写了一小段代码。
package resourceslock
import (
"errors"
"sync"
)
var (
Lock resourcesLock = resourcesLock{
lockerMapMtx: new(sync.Mutex),
lockerMap: make(map[ResourceTopic]chan void),
topicWaitlistMtx: make(map[ResourceTopic]*sync.Mutex),
topicWaitlist: make(map[ResourceTopic][]chan bool),
} // universal lock
voidMsg void = void{}
)
type ResourceTopic string
type ProtectCondition func() bool
type ReloadFunc func() (interface{}, error)
type void struct{}
type resourcesLock struct {
lockerMapMtx *sync.Mutex
topicWaitlistMtx map[ResourceTopic]*sync.Mutex
lockerMap map[ResourceTopic](chan void)
topicWaitlist map[ResourceTopic][](chan bool)
}
func (self *resourcesLock) Protect(topic ResourceTopic, cond *ProtectCondition, reload *ReloadFunc) error {
if (*cond)() == true {
return nil
}
var err error
self.createLockChn(topic)
select {
case <-self.lockerMap[topic]:
_, err = (*reload)()
if err != nil {
self.notifyError(topic)
} else {
self.notifySuccess(topic)
}
self.lockerMap[topic] <- voidMsg
default:
if !<-self.addToWaitlist(topic) {
err = errors.New("Resource is not refreshed.")
}
}
return err
}
func (self *resourcesLock) createLockChn(topic ResourceTopic) {
self.lockerMapMtx.Lock()
_, ok := self.lockerMap[topic]
if !ok {
ch := make(chan void, 1)
ch <- voidMsg
self.lockerMap[topic] = ch
self.topicWaitlistMtx[topic] = new(sync.Mutex)
}
self.lockerMapMtx.Unlock()
}
func (self *resourcesLock) addToWaitlist(topic ResourceTopic) <-chan bool {
ch := make(chan bool)
self.topicWaitlistMtx[topic].Lock()
self.topicWaitlist[topic] = append(self.topicWaitlist[topic], ch)
self.topicWaitlistMtx[topic].Unlock()
return ch
}
func (self *resourcesLock) notifyError(topic ResourceTopic) {
self.notify(topic, false)
}
func (self *resourcesLock) notifySuccess(topic ResourceTopic) {
self.notify(topic, true)
}
func (self *resourcesLock) notify(topic ResourceTopic, suc bool) {
self.topicWaitlistMtx[topic].Lock()
for i, c := range self.topicWaitlist[topic] {
if suc {
c <- true
} else {
c <- false
}
self.topicWaitlist[topic][i] = nil
}
self.topicWaitlist[topic] = self.topicWaitlist[topic][:0]
self.topicWaitlistMtx[topic].Unlock()
}
有疑问加站长微信联系(非本文作者)