From e1e00ed0683bd3b477a855f30c343d613192dfd6 Mon Sep 17 00:00:00 2001 From: Mrs4s <1844812067@qq.com> Date: Tue, 4 Aug 2020 14:40:12 +0800 Subject: [PATCH] fix ttl. --- client/decoders.go | 8 ++-- client/entities.go | 2 +- utils/ttl.go | 114 +++++++++++++++++++++++++++++++-------------- 3 files changed, 84 insertions(+), 40 deletions(-) diff --git a/client/decoders.go b/client/decoders.go index c6f09e21..b9646026 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -15,6 +15,7 @@ import ( "github.com/Mrs4s/MiraiGo/client/pb/structmsg" "github.com/Mrs4s/MiraiGo/utils" "github.com/golang/protobuf/proto" + "strconv" "sync" "sync/atomic" "time" @@ -235,12 +236,13 @@ func decodeMessageSvcPacket(c *QQClient, _ uint16, payload []byte) (interface{}, return nil, nil } if friend.msgSeqList == nil { - friend.msgSeqList = utils.NewTTList(60) + friend.msgSeqList = utils.NewCache(time.Second * 5) } - if friend.msgSeqList.Any(func(i interface{}) bool { return i.(int32) == message.Head.MsgSeq }) { + strSeq := strconv.FormatInt(int64(message.Head.MsgSeq), 10) + if _, ok := friend.msgSeqList.Get(strSeq); ok { continue } - friend.msgSeqList.Add(message.Head.MsgSeq) + friend.msgSeqList.Add(strSeq, 0, time.Second*15) c.dispatchFriendMessage(c.parsePrivateMessage(message)) case 187: _, pkt := c.buildSystemMsgNewFriendPacket() diff --git a/client/entities.go b/client/entities.go index cafffec1..2d45b4cf 100644 --- a/client/entities.go +++ b/client/entities.go @@ -37,7 +37,7 @@ type ( Remark string FaceId int16 - msgSeqList *utils.TTList + msgSeqList *utils.Cache } FriendListResponse struct { diff --git a/utils/ttl.go b/utils/ttl.go index 65766f49..ad33332f 100644 --- a/utils/ttl.go +++ b/utils/ttl.go @@ -5,54 +5,96 @@ import ( "time" ) -type TTList struct { - list []*item - lock sync.Mutex +// https://github.com/Konstantin8105/SimpleTTL +// entry - typical element of cache +type entry struct { + value interface{} + expiry *time.Time } -type item struct { - i interface{} - lastAccess int64 +// Cache - simple implementation of cache +// More information: https://en.wikipedia.org/wiki/Time_to_live +type Cache struct { + timeTTL time.Duration + cache map[string]*entry + lock *sync.RWMutex } -func NewTTList(ttl int64) *TTList { - l := &TTList{} +// NewCache - initialization of new cache. +// For avoid mistake - minimal time to live is 1 minute. +// For simplification, - key is string and cache haven`t stop method +func NewCache(interval time.Duration) *Cache { + if interval < time.Second { + interval = time.Second + } + cache := &Cache{ + timeTTL: interval, + cache: make(map[string]*entry), + lock: &sync.RWMutex{}, + } go func() { - for now := range time.Tick(time.Second * 5) { - l.lock.Lock() - pos := 0 - for _, i := range l.list { - if now.Unix()-i.lastAccess > ttl { - l.list = append(l.list[:pos], l.list[pos+1:]...) - if pos > 0 { - pos++ - } + ticker := time.NewTicker(cache.timeTTL) + for { + // wait of ticker + now := <-ticker.C + + // remove entry outside TTL + cache.lock.Lock() + for id, entry := range cache.cache { + if entry.expiry != nil && entry.expiry.Before(now) { + delete(cache.cache, id) } - pos++ } - l.lock.Unlock() + cache.lock.Unlock() } }() - return l + return cache } -func (l *TTList) Add(i interface{}) { - l.lock.Lock() - defer l.lock.Unlock() - l.list = append(l.list, &item{ - i: i, - lastAccess: time.Now().Unix(), - }) +// Count - return amount element of TTL map. +func (cache *Cache) Count() int { + cache.lock.RLock() + defer cache.lock.RUnlock() + + return len(cache.cache) } -func (l *TTList) Any(filter func(i interface{}) bool) bool { - l.lock.Lock() - defer l.lock.Unlock() - for _, it := range l.list { - if filter(it.i) { - it.lastAccess = time.Now().Unix() - return true - } +// Get - return value from cache +func (cache *Cache) Get(key string) (interface{}, bool) { + cache.lock.RLock() + defer cache.lock.RUnlock() + + e, ok := cache.cache[key] + + if ok && e.expiry != nil && e.expiry.After(time.Now()) { + return e.value, true } - return false + return nil, false +} + +// Add - add key/value in cache +func (cache *Cache) Add(key string, value interface{}, ttl time.Duration) { + cache.lock.Lock() + defer cache.lock.Unlock() + + expiry := time.Now().Add(ttl) + + cache.cache[key] = &entry{ + value: value, + expiry: &expiry, + } +} + +// GetKeys - return all keys of cache map +func (cache *Cache) GetKeys() []interface{} { + cache.lock.RLock() + defer cache.lock.RUnlock() + + keys := make([]interface{}, len(cache.cache)) + var i int + for k := range cache.cache { + keys[i] = k + i++ + } + return keys }