mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-12-08 19:00:54 +08:00
实现WAF
This commit is contained in:
186
internal/grids/cell.go
Normal file
186
internal/grids/cell.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package grids
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cell struct {
|
||||
LimitSize int64
|
||||
LimitCount int
|
||||
|
||||
mapping map[uint64]*Item // key => item
|
||||
list *List // { item1, item2, ... }
|
||||
totalBytes int64
|
||||
locker sync.RWMutex
|
||||
}
|
||||
|
||||
func NewCell() *Cell {
|
||||
return &Cell{
|
||||
mapping: map[uint64]*Item{},
|
||||
list: NewList(),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Cell) Write(hashKey uint64, item *Item) {
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
this.locker.Lock()
|
||||
|
||||
oldItem, ok := this.mapping[hashKey]
|
||||
if ok {
|
||||
this.list.Remove(oldItem)
|
||||
|
||||
if this.LimitSize > 0 {
|
||||
this.totalBytes -= oldItem.Size()
|
||||
}
|
||||
}
|
||||
|
||||
// limit count
|
||||
if this.LimitCount > 0 && len(this.mapping) >= this.LimitCount {
|
||||
this.locker.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// trim memory
|
||||
size := item.Size()
|
||||
shouldTrim := false
|
||||
if this.LimitSize > 0 && this.LimitSize < this.totalBytes+size {
|
||||
this.Trim()
|
||||
shouldTrim = true
|
||||
}
|
||||
|
||||
// compare again
|
||||
if shouldTrim {
|
||||
if this.LimitSize > 0 && this.LimitSize < this.totalBytes+size {
|
||||
this.locker.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.totalBytes += size
|
||||
|
||||
this.list.Add(item)
|
||||
this.mapping[hashKey] = item
|
||||
|
||||
this.locker.Unlock()
|
||||
}
|
||||
|
||||
func (this *Cell) Increase64(key []byte, expireAt int64, hashKey uint64, delta int64) (result int64) {
|
||||
this.locker.Lock()
|
||||
item, ok := this.mapping[hashKey]
|
||||
if ok {
|
||||
// reset to zero if expired
|
||||
if item.ExpireAt < time.Now().Unix() {
|
||||
item.ValueInt64 = 0
|
||||
item.ExpireAt = expireAt
|
||||
}
|
||||
item.IncreaseInt64(delta)
|
||||
result = item.ValueInt64
|
||||
} else {
|
||||
item := NewItem(key, ItemInt64)
|
||||
item.ValueInt64 = delta
|
||||
item.ExpireAt = expireAt
|
||||
this.mapping[hashKey] = item
|
||||
result = delta
|
||||
}
|
||||
this.locker.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (this *Cell) Read(hashKey uint64) *Item {
|
||||
this.locker.Lock()
|
||||
|
||||
item, ok := this.mapping[hashKey]
|
||||
if ok {
|
||||
this.list.Remove(item)
|
||||
this.list.Add(item)
|
||||
|
||||
this.locker.Unlock()
|
||||
|
||||
if item.ExpireAt < time.Now().Unix() {
|
||||
return nil
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
this.locker.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Cell) Stat() *CellStat {
|
||||
this.locker.RLock()
|
||||
defer this.locker.RUnlock()
|
||||
|
||||
return &CellStat{
|
||||
TotalBytes: this.totalBytes,
|
||||
CountItems: len(this.mapping),
|
||||
}
|
||||
}
|
||||
|
||||
// trim NOT ACTIVE items
|
||||
// should called in locker context
|
||||
func (this *Cell) Trim() {
|
||||
l := len(this.mapping)
|
||||
if l == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
inactiveSize := int(math.Ceil(float64(l) / 10)) // trim 10% items
|
||||
this.list.Range(func(item *Item) (goNext bool) {
|
||||
inactiveSize--
|
||||
delete(this.mapping, item.HashKey())
|
||||
this.list.Remove(item)
|
||||
this.totalBytes -= item.Size()
|
||||
return inactiveSize > 0
|
||||
})
|
||||
}
|
||||
|
||||
func (this *Cell) Delete(hashKey uint64) {
|
||||
this.locker.Lock()
|
||||
item, ok := this.mapping[hashKey]
|
||||
if ok {
|
||||
delete(this.mapping, hashKey)
|
||||
this.list.Remove(item)
|
||||
this.totalBytes -= item.Size()
|
||||
}
|
||||
this.locker.Unlock()
|
||||
}
|
||||
|
||||
// range all items in the cell
|
||||
func (this *Cell) Range(f func(item *Item)) {
|
||||
this.locker.Lock()
|
||||
for _, item := range this.mapping {
|
||||
f(item)
|
||||
}
|
||||
this.locker.Unlock()
|
||||
}
|
||||
|
||||
func (this *Cell) Recycle() {
|
||||
this.locker.Lock()
|
||||
if len(this.mapping) == 0 {
|
||||
this.locker.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
for key, item := range this.mapping {
|
||||
if item.ExpireAt < timestamp {
|
||||
delete(this.mapping, key)
|
||||
this.list.Remove(item)
|
||||
this.totalBytes -= item.Size()
|
||||
}
|
||||
}
|
||||
|
||||
this.locker.Unlock()
|
||||
}
|
||||
|
||||
func (this *Cell) Reset() {
|
||||
this.locker.Lock()
|
||||
this.list.Reset()
|
||||
this.mapping = map[uint64]*Item{}
|
||||
this.totalBytes = 0
|
||||
this.locker.Unlock()
|
||||
}
|
||||
Reference in New Issue
Block a user