mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-05 17:40:26 +08:00
226 lines
4.8 KiB
Go
226 lines
4.8 KiB
Go
|
|
package grids
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"compress/gzip"
|
||
|
|
"github.com/iwind/TeaGo/logs"
|
||
|
|
"github.com/iwind/TeaGo/timers"
|
||
|
|
"math"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Memory Cache Grid
|
||
|
|
//
|
||
|
|
// | Grid |
|
||
|
|
// | cell1, cell2, ..., cell1024 |
|
||
|
|
// | item1, item2, ..., item1000000 |
|
||
|
|
type Grid struct {
|
||
|
|
cells []*Cell
|
||
|
|
countCells uint64
|
||
|
|
|
||
|
|
recycleIndex int
|
||
|
|
recycleLooper *timers.Looper
|
||
|
|
recycleInterval int
|
||
|
|
|
||
|
|
gzipLevel int
|
||
|
|
|
||
|
|
limitSize int64
|
||
|
|
limitCount int
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewGrid(countCells int, opt ...interface{}) *Grid {
|
||
|
|
grid := &Grid{
|
||
|
|
recycleIndex: -1,
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, o := range opt {
|
||
|
|
switch x := o.(type) {
|
||
|
|
case *CompressOpt:
|
||
|
|
grid.gzipLevel = x.Level
|
||
|
|
case *LimitSizeOpt:
|
||
|
|
grid.limitSize = x.Size
|
||
|
|
case *LimitCountOpt:
|
||
|
|
grid.limitCount = x.Count
|
||
|
|
case *RecycleIntervalOpt:
|
||
|
|
grid.recycleInterval = x.Interval
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
cells := []*Cell{}
|
||
|
|
if countCells <= 0 {
|
||
|
|
countCells = 1
|
||
|
|
} else if countCells > 100*10000 {
|
||
|
|
countCells = 100 * 10000
|
||
|
|
}
|
||
|
|
for i := 0; i < countCells; i++ {
|
||
|
|
cell := NewCell()
|
||
|
|
cell.LimitSize = int64(math.Floor(float64(grid.limitSize) / float64(countCells)))
|
||
|
|
cell.LimitCount = int(math.Floor(float64(grid.limitCount)) / float64(countCells))
|
||
|
|
|
||
|
|
cells = append(cells, cell)
|
||
|
|
}
|
||
|
|
grid.cells = cells
|
||
|
|
grid.countCells = uint64(len(cells))
|
||
|
|
|
||
|
|
grid.recycleTimer()
|
||
|
|
return grid
|
||
|
|
}
|
||
|
|
|
||
|
|
// get all cells in the grid
|
||
|
|
func (this *Grid) Cells() []*Cell {
|
||
|
|
return this.cells
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) WriteItem(item *Item) {
|
||
|
|
if this.countCells <= 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
hashKey := item.HashKey()
|
||
|
|
this.cellForHashKey(hashKey).Write(hashKey, item)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) WriteInt64(key []byte, value int64, lifeSeconds int64) {
|
||
|
|
this.WriteItem(&Item{
|
||
|
|
Key: key,
|
||
|
|
Type: ItemInt64,
|
||
|
|
ValueInt64: value,
|
||
|
|
ExpireAt: time.Now().Unix() + lifeSeconds,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) IncreaseInt64(key []byte, delta int64, lifeSeconds int64) (result int64) {
|
||
|
|
hashKey := HashKey(key)
|
||
|
|
return this.cellForHashKey(hashKey).Increase64(key, time.Now().Unix()+lifeSeconds, hashKey, delta)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) WriteString(key []byte, value string, lifeSeconds int64) {
|
||
|
|
this.WriteBytes(key, []byte(value), lifeSeconds)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) WriteBytes(key []byte, value []byte, lifeSeconds int64) {
|
||
|
|
isCompressed := false
|
||
|
|
if this.gzipLevel != gzip.NoCompression {
|
||
|
|
buf := bytes.NewBuffer([]byte{})
|
||
|
|
writer, err := gzip.NewWriterLevel(buf, this.gzipLevel)
|
||
|
|
if err != nil {
|
||
|
|
logs.Error(err)
|
||
|
|
this.WriteItem(&Item{
|
||
|
|
Key: key,
|
||
|
|
Type: ItemBytes,
|
||
|
|
ValueBytes: value,
|
||
|
|
ExpireAt: time.Now().Unix() + lifeSeconds,
|
||
|
|
})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
_, err = writer.Write([]byte(value))
|
||
|
|
if err != nil {
|
||
|
|
logs.Error(err)
|
||
|
|
this.WriteItem(&Item{
|
||
|
|
Key: key,
|
||
|
|
Type: ItemBytes,
|
||
|
|
ValueBytes: value,
|
||
|
|
ExpireAt: time.Now().Unix() + lifeSeconds,
|
||
|
|
})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
err = writer.Close()
|
||
|
|
if err != nil {
|
||
|
|
logs.Error(err)
|
||
|
|
this.WriteItem(&Item{
|
||
|
|
Key: key,
|
||
|
|
Type: ItemBytes,
|
||
|
|
ValueBytes: value,
|
||
|
|
ExpireAt: time.Now().Unix() + lifeSeconds,
|
||
|
|
})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
value = buf.Bytes()
|
||
|
|
isCompressed = true
|
||
|
|
}
|
||
|
|
|
||
|
|
this.WriteItem(&Item{
|
||
|
|
Key: key,
|
||
|
|
Type: ItemBytes,
|
||
|
|
ValueBytes: value,
|
||
|
|
ExpireAt: time.Now().Unix() + lifeSeconds,
|
||
|
|
IsCompressed: isCompressed,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) WriteInterface(key []byte, value interface{}, lifeSeconds int64) {
|
||
|
|
this.WriteItem(&Item{
|
||
|
|
Key: key,
|
||
|
|
Type: ItemInterface,
|
||
|
|
ValueInterface: value,
|
||
|
|
ExpireAt: time.Now().Unix() + lifeSeconds,
|
||
|
|
IsCompressed: false,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) Read(key []byte) *Item {
|
||
|
|
if this.countCells <= 0 {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
hashKey := HashKey(key)
|
||
|
|
return this.cellForHashKey(hashKey).Read(hashKey)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) Stat() *Stat {
|
||
|
|
stat := &Stat{}
|
||
|
|
for _, cell := range this.cells {
|
||
|
|
cellStat := cell.Stat()
|
||
|
|
stat.CountItems += cellStat.CountItems
|
||
|
|
stat.TotalBytes += cellStat.TotalBytes
|
||
|
|
}
|
||
|
|
return stat
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) Delete(key []byte) {
|
||
|
|
if this.countCells <= 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
hashKey := HashKey(key)
|
||
|
|
this.cellForHashKey(hashKey).Delete(hashKey)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) Reset() {
|
||
|
|
for _, cell := range this.cells {
|
||
|
|
cell.Reset()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) Destroy() {
|
||
|
|
if this.recycleLooper != nil {
|
||
|
|
this.recycleLooper.Stop()
|
||
|
|
this.recycleLooper = nil
|
||
|
|
}
|
||
|
|
this.cells = nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) cellForHashKey(hashKey uint64) *Cell {
|
||
|
|
if hashKey < 0 {
|
||
|
|
return this.cells[-hashKey%this.countCells]
|
||
|
|
} else {
|
||
|
|
return this.cells[hashKey%this.countCells]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (this *Grid) recycleTimer() {
|
||
|
|
duration := 1 * time.Minute
|
||
|
|
if this.recycleInterval > 0 {
|
||
|
|
duration = time.Duration(this.recycleInterval) * time.Second
|
||
|
|
}
|
||
|
|
this.recycleLooper = timers.Loop(duration, func(looper *timers.Looper) {
|
||
|
|
if this.countCells == 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
this.recycleIndex++
|
||
|
|
if this.recycleIndex > int(this.countCells-1) {
|
||
|
|
this.recycleIndex = 0
|
||
|
|
}
|
||
|
|
this.cells[this.recycleIndex].Recycle()
|
||
|
|
})
|
||
|
|
}
|