mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 16:00:25 +08:00 
			
		
		
		
	实现自动将热点数据加载到内存中
This commit is contained in:
		
							
								
								
									
										10
									
								
								internal/caches/hot_item.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								internal/caches/hot_item.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package caches
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type HotItem struct {
 | 
				
			||||||
 | 
						Key       string
 | 
				
			||||||
 | 
						ExpiresAt int64
 | 
				
			||||||
 | 
						Hits      uint32
 | 
				
			||||||
 | 
						Status int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,6 +9,9 @@ type Reader interface {
 | 
				
			|||||||
	// TypeName 类型名称
 | 
						// TypeName 类型名称
 | 
				
			||||||
	TypeName() string
 | 
						TypeName() string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ExpiresAt 过期时间
 | 
				
			||||||
 | 
						ExpiresAt() int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Status 状态码
 | 
						// Status 状态码
 | 
				
			||||||
	Status() int
 | 
						Status() int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import (
 | 
				
			|||||||
type FileReader struct {
 | 
					type FileReader struct {
 | 
				
			||||||
	fp *os.File
 | 
						fp *os.File
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expiresAt    int64
 | 
				
			||||||
	status       int
 | 
						status       int
 | 
				
			||||||
	headerOffset int64
 | 
						headerOffset int64
 | 
				
			||||||
	headerSize   int
 | 
						headerSize   int
 | 
				
			||||||
@@ -43,6 +44,8 @@ func (this *FileReader) Init() error {
 | 
				
			|||||||
		return ErrNotFound
 | 
							return ErrNotFound
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.expiresAt = int64(binary.BigEndian.Uint32(buf[:SizeExpiresAt]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	status := types.Int(string(buf[SizeExpiresAt : SizeExpiresAt+SizeStatus]))
 | 
						status := types.Int(string(buf[SizeExpiresAt : SizeExpiresAt+SizeStatus]))
 | 
				
			||||||
	if status < 100 || status > 999 {
 | 
						if status < 100 || status > 999 {
 | 
				
			||||||
		return errors.New("invalid status")
 | 
							return errors.New("invalid status")
 | 
				
			||||||
@@ -78,6 +81,10 @@ func (this *FileReader) TypeName() string {
 | 
				
			|||||||
	return "disk"
 | 
						return "disk"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *FileReader) ExpiresAt() int64 {
 | 
				
			||||||
 | 
						return this.expiresAt
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *FileReader) Status() int {
 | 
					func (this *FileReader) Status() int {
 | 
				
			||||||
	return this.status
 | 
						return this.status
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,10 @@ func (this *MemoryReader) TypeName() string {
 | 
				
			|||||||
	return "memory"
 | 
						return "memory"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *MemoryReader) ExpiresAt() int64 {
 | 
				
			||||||
 | 
						return this.item.ExpiredAt
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *MemoryReader) Status() int {
 | 
					func (this *MemoryReader) Status() int {
 | 
				
			||||||
	return this.item.Status
 | 
						return this.item.Status
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
@@ -40,6 +41,10 @@ const (
 | 
				
			|||||||
	SizeMeta = SizeExpiresAt + SizeStatus + SizeURLLength + SizeHeaderLength + SizeBodyLength
 | 
						SizeMeta = SizeExpiresAt + SizeStatus + SizeURLLength + SizeHeaderLength + SizeBodyLength
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						HotItemSize = 1024
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FileStorage 文件缓存
 | 
					// FileStorage 文件缓存
 | 
				
			||||||
//   文件结构:
 | 
					//   文件结构:
 | 
				
			||||||
//    [expires time] | [ status ] | [url length] | [header length] | [body length] | [url] [header data] [body data]
 | 
					//    [expires time] | [ status ] | [url length] | [header length] | [body length] | [url] [header data] [body data]
 | 
				
			||||||
@@ -52,13 +57,20 @@ type FileStorage struct {
 | 
				
			|||||||
	list          ListInterface
 | 
						list          ListInterface
 | 
				
			||||||
	writingKeyMap map[string]bool // key => bool
 | 
						writingKeyMap map[string]bool // key => bool
 | 
				
			||||||
	locker        sync.RWMutex
 | 
						locker        sync.RWMutex
 | 
				
			||||||
	ticker        *utils.Ticker
 | 
						purgeTicker   *utils.Ticker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hotMap       map[string]*HotItem // key => count
 | 
				
			||||||
 | 
						hotMapLocker sync.Mutex
 | 
				
			||||||
 | 
						lastHotSize  int
 | 
				
			||||||
 | 
						hotTicker    *utils.Ticker
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewFileStorage(policy *serverconfigs.HTTPCachePolicy) *FileStorage {
 | 
					func NewFileStorage(policy *serverconfigs.HTTPCachePolicy) *FileStorage {
 | 
				
			||||||
	return &FileStorage{
 | 
						return &FileStorage{
 | 
				
			||||||
		policy:        policy,
 | 
							policy:        policy,
 | 
				
			||||||
		writingKeyMap: map[string]bool{},
 | 
							writingKeyMap: map[string]bool{},
 | 
				
			||||||
 | 
							hotMap:        map[string]*HotItem{},
 | 
				
			||||||
 | 
							lastHotSize:   -1,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -191,8 +203,12 @@ func (this *FileStorage) Init() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *FileStorage) OpenReader(key string) (Reader, error) {
 | 
					func (this *FileStorage) OpenReader(key string) (Reader, error) {
 | 
				
			||||||
 | 
						return this.openReader(key, true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *FileStorage) openReader(key string, allowMemory bool) (Reader, error) {
 | 
				
			||||||
	// 先尝试内存缓存
 | 
						// 先尝试内存缓存
 | 
				
			||||||
	if this.memoryStorage != nil {
 | 
						if allowMemory && this.memoryStorage != nil {
 | 
				
			||||||
		reader, err := this.memoryStorage.OpenReader(key)
 | 
							reader, err := this.memoryStorage.OpenReader(key)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			return reader, err
 | 
								return reader, err
 | 
				
			||||||
@@ -237,12 +253,40 @@ func (this *FileStorage) OpenReader(key string) (Reader, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// 增加点击量
 | 
						// 增加点击量
 | 
				
			||||||
	// 1/1000采样
 | 
						// 1/1000采样
 | 
				
			||||||
	// TODO 考虑是否在缓存策略里设置
 | 
						if allowMemory {
 | 
				
			||||||
	if rands.Int(0, 1000) == 0 {
 | 
							var rate = this.policy.PersistenceHitSampleRate
 | 
				
			||||||
		var hitErr = this.list.IncreaseHit(hash)
 | 
							if rate <= 0 {
 | 
				
			||||||
		if hitErr != nil {
 | 
								rate = 1000
 | 
				
			||||||
			// 此错误可以忽略
 | 
							}
 | 
				
			||||||
			remotelogs.Error("CACHE", "increase hit failed: "+hitErr.Error())
 | 
							if this.lastHotSize == 0 {
 | 
				
			||||||
 | 
								// 自动降低采样率来增加热点数据的缓存几率
 | 
				
			||||||
 | 
								rate = rate / 10
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if rands.Int(0, rate) == 0 {
 | 
				
			||||||
 | 
								var hitErr = this.list.IncreaseHit(hash)
 | 
				
			||||||
 | 
								if hitErr != nil {
 | 
				
			||||||
 | 
									// 此错误可以忽略
 | 
				
			||||||
 | 
									remotelogs.Error("CACHE", "increase hit failed: "+hitErr.Error())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 增加到热点
 | 
				
			||||||
 | 
								// 这里不收录缓存尺寸过大的文件
 | 
				
			||||||
 | 
								if this.memoryStorage != nil && reader.BodySize() > 0 && reader.BodySize() < 128*1024*1024 {
 | 
				
			||||||
 | 
									this.hotMapLocker.Lock()
 | 
				
			||||||
 | 
									hotItem, ok := this.hotMap[key]
 | 
				
			||||||
 | 
									if ok {
 | 
				
			||||||
 | 
										hotItem.Hits++
 | 
				
			||||||
 | 
										hotItem.ExpiresAt = reader.expiresAt
 | 
				
			||||||
 | 
									} else if len(this.hotMap) < HotItemSize { // 控制数量
 | 
				
			||||||
 | 
										this.hotMap[key] = &HotItem{
 | 
				
			||||||
 | 
											Key:       key,
 | 
				
			||||||
 | 
											ExpiresAt: reader.ExpiresAt(),
 | 
				
			||||||
 | 
											Status:    reader.Status(),
 | 
				
			||||||
 | 
											Hits:      1,
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									this.hotMapLocker.Unlock()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -574,8 +618,11 @@ func (this *FileStorage) Stop() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_ = this.list.Reset()
 | 
						_ = this.list.Reset()
 | 
				
			||||||
	if this.ticker != nil {
 | 
						if this.purgeTicker != nil {
 | 
				
			||||||
		this.ticker.Stop()
 | 
							this.purgeTicker.Stop()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if this.hotTicker != nil {
 | 
				
			||||||
 | 
							this.hotTicker.Stop()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_ = this.list.Close()
 | 
						_ = this.list.Close()
 | 
				
			||||||
@@ -645,19 +692,32 @@ func (this *FileStorage) initList() error {
 | 
				
			|||||||
			autoPurgeInterval = 10
 | 
								autoPurgeInterval = 10
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this.ticker = utils.NewTicker(time.Duration(autoPurgeInterval) * time.Second)
 | 
						this.purgeTicker = utils.NewTicker(time.Duration(autoPurgeInterval) * time.Second)
 | 
				
			||||||
	events.On(events.EventQuit, func() {
 | 
						events.On(events.EventQuit, func() {
 | 
				
			||||||
		remotelogs.Println("CACHE", "quit clean timer")
 | 
							remotelogs.Println("CACHE", "quit clean timer")
 | 
				
			||||||
		var ticker = this.ticker
 | 
							var ticker = this.purgeTicker
 | 
				
			||||||
		if ticker != nil {
 | 
							if ticker != nil {
 | 
				
			||||||
			ticker.Stop()
 | 
								ticker.Stop()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		for this.ticker.Next() {
 | 
							for this.purgeTicker.Next() {
 | 
				
			||||||
			var tr = trackers.Begin("FILE_CACHE_STORAGE_PURGE_LOOP")
 | 
								trackers.Run("FILE_CACHE_STORAGE_PURGE_LOOP", func() {
 | 
				
			||||||
			this.purgeLoop()
 | 
									this.purgeLoop()
 | 
				
			||||||
			tr.End()
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 热点处理任务
 | 
				
			||||||
 | 
						this.hotTicker = utils.NewTicker(1 * time.Minute)
 | 
				
			||||||
 | 
						if Tea.IsTesting() {
 | 
				
			||||||
 | 
							this.hotTicker = utils.NewTicker(10 * time.Second)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for this.hotTicker.Next() {
 | 
				
			||||||
 | 
								trackers.Run("FILE_CACHE_STORAGE_HOT_LOOP", func() {
 | 
				
			||||||
 | 
									this.hotLoop()
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -842,6 +902,102 @@ func (this *FileStorage) purgeLoop() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 热点数据任务
 | 
				
			||||||
 | 
					func (this *FileStorage) hotLoop() {
 | 
				
			||||||
 | 
						var memoryStorage = this.memoryStorage
 | 
				
			||||||
 | 
						if memoryStorage == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.hotMapLocker.Lock()
 | 
				
			||||||
 | 
						if len(this.hotMap) == 0 {
 | 
				
			||||||
 | 
							this.hotMapLocker.Unlock()
 | 
				
			||||||
 | 
							this.lastHotSize = 0
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.lastHotSize = len(this.hotMap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var result = []*HotItem{} // [ {key: ..., hits: ...}, ... ]
 | 
				
			||||||
 | 
						for _, v := range this.hotMap {
 | 
				
			||||||
 | 
							result = append(result, v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.hotMap = map[string]*HotItem{}
 | 
				
			||||||
 | 
						this.hotMapLocker.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 取Top10
 | 
				
			||||||
 | 
						if len(result) > 0 {
 | 
				
			||||||
 | 
							sort.Slice(result, func(i, j int) bool {
 | 
				
			||||||
 | 
								return result[i].Hits > result[j].Hits
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							var size = 1
 | 
				
			||||||
 | 
							if len(result) < 10 {
 | 
				
			||||||
 | 
								size = 1
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								size = len(result) / 10
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var buf = make([]byte, 32*1024)
 | 
				
			||||||
 | 
							for _, item := range result[:size] {
 | 
				
			||||||
 | 
								reader, err := this.openReader(item.Key, false)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if reader == nil {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if reader.ExpiresAt() <= time.Now().Unix() {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								writer, err := this.memoryStorage.openWriter(item.Key, item.ExpiresAt, item.Status, false)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if !CanIgnoreErr(err) {
 | 
				
			||||||
 | 
										remotelogs.Error("CACHE", "transfer hot item failed: "+err.Error())
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									_ = reader.Close()
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if writer == nil {
 | 
				
			||||||
 | 
									_ = reader.Close()
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err = reader.ReadHeader(buf, func(n int) (goNext bool, err error) {
 | 
				
			||||||
 | 
									_, err = writer.WriteHeader(buf[:n])
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									_ = reader.Close()
 | 
				
			||||||
 | 
									_ = writer.Discard()
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err = reader.ReadBody(buf, func(n int) (goNext bool, err error) {
 | 
				
			||||||
 | 
									_, err = writer.Write(buf[:n])
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									_ = reader.Close()
 | 
				
			||||||
 | 
									_ = writer.Discard()
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.memoryStorage.AddToList(&Item{
 | 
				
			||||||
 | 
									Type:       writer.ItemType(),
 | 
				
			||||||
 | 
									Key:        item.Key,
 | 
				
			||||||
 | 
									ExpiredAt:  item.ExpiresAt,
 | 
				
			||||||
 | 
									HeaderSize: writer.HeaderSize(),
 | 
				
			||||||
 | 
									BodySize:   writer.BodySize(),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_ = reader.Close()
 | 
				
			||||||
 | 
								_ = writer.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *FileStorage) readToBuff(fp *os.File, buf []byte) (ok bool, err error) {
 | 
					func (this *FileStorage) readToBuff(fp *os.File, buf []byte) (ok bool, err error) {
 | 
				
			||||||
	n, err := fp.Read(buf)
 | 
						n, err := fp.Read(buf)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -144,6 +144,10 @@ func (this *MemoryStorage) OpenReader(key string) (Reader, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// OpenWriter 打开缓存写入器等待写入
 | 
					// OpenWriter 打开缓存写入器等待写入
 | 
				
			||||||
func (this *MemoryStorage) OpenWriter(key string, expiredAt int64, status int) (Writer, error) {
 | 
					func (this *MemoryStorage) OpenWriter(key string, expiredAt int64, status int) (Writer, error) {
 | 
				
			||||||
 | 
						return this.openWriter(key, expiredAt, status, true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *MemoryStorage) openWriter(key string, expiredAt int64, status int, isDirty bool) (Writer, error) {
 | 
				
			||||||
	this.locker.Lock()
 | 
						this.locker.Lock()
 | 
				
			||||||
	defer this.locker.Unlock()
 | 
						defer this.locker.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -187,7 +191,7 @@ func (this *MemoryStorage) OpenWriter(key string, expiredAt int64, status int) (
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	isWriting = true
 | 
						isWriting = true
 | 
				
			||||||
	return NewMemoryWriter(this, key, expiredAt, status, func() {
 | 
						return NewMemoryWriter(this, key, expiredAt, status, isDirty, func() {
 | 
				
			||||||
		this.locker.Lock()
 | 
							this.locker.Lock()
 | 
				
			||||||
		delete(this.writingKeyMap, key)
 | 
							delete(this.writingKeyMap, key)
 | 
				
			||||||
		this.locker.Unlock()
 | 
							this.locker.Unlock()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,13 +13,14 @@ type MemoryWriter struct {
 | 
				
			|||||||
	headerSize int64
 | 
						headerSize int64
 | 
				
			||||||
	bodySize   int64
 | 
						bodySize   int64
 | 
				
			||||||
	status     int
 | 
						status     int
 | 
				
			||||||
 | 
						isDirty    bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hash    uint64
 | 
						hash    uint64
 | 
				
			||||||
	item    *MemoryItem
 | 
						item    *MemoryItem
 | 
				
			||||||
	endFunc func()
 | 
						endFunc func()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewMemoryWriter(memoryStorage *MemoryStorage, key string, expiredAt int64, status int, endFunc func()) *MemoryWriter {
 | 
					func NewMemoryWriter(memoryStorage *MemoryStorage, key string, expiredAt int64, status int, isDirty bool, endFunc func()) *MemoryWriter {
 | 
				
			||||||
	w := &MemoryWriter{
 | 
						w := &MemoryWriter{
 | 
				
			||||||
		storage:   memoryStorage,
 | 
							storage:   memoryStorage,
 | 
				
			||||||
		key:       key,
 | 
							key:       key,
 | 
				
			||||||
@@ -30,6 +31,7 @@ func NewMemoryWriter(memoryStorage *MemoryStorage, key string, expiredAt int64,
 | 
				
			|||||||
			Status:     status,
 | 
								Status:     status,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		status:  status,
 | 
							status:  status,
 | 
				
			||||||
 | 
							isDirty: isDirty,
 | 
				
			||||||
		endFunc: endFunc,
 | 
							endFunc: endFunc,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	w.hash = w.calculateHash(key)
 | 
						w.hash = w.calculateHash(key)
 | 
				
			||||||
@@ -73,11 +75,13 @@ func (this *MemoryWriter) Close() error {
 | 
				
			|||||||
	this.storage.locker.Lock()
 | 
						this.storage.locker.Lock()
 | 
				
			||||||
	this.item.IsDone = true
 | 
						this.item.IsDone = true
 | 
				
			||||||
	this.storage.valuesMap[this.hash] = this.item
 | 
						this.storage.valuesMap[this.hash] = this.item
 | 
				
			||||||
	if this.storage.parentStorage != nil {
 | 
						if this.isDirty {
 | 
				
			||||||
		select {
 | 
							if this.storage.parentStorage != nil {
 | 
				
			||||||
		case this.storage.dirtyChan <- this.key:
 | 
								select {
 | 
				
			||||||
		default:
 | 
								case this.storage.dirtyChan <- this.key:
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this.storage.locker.Unlock()
 | 
						this.storage.locker.Unlock()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,12 @@ func Begin(label string) *tracker {
 | 
				
			|||||||
	return &tracker{label: label, startTime: time.Now()}
 | 
						return &tracker{label: label, startTime: time.Now()}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Run(label string, f func()) {
 | 
				
			||||||
 | 
						var tr = Begin(label)
 | 
				
			||||||
 | 
						f()
 | 
				
			||||||
 | 
						tr.End()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *tracker) End() {
 | 
					func (this *tracker) End() {
 | 
				
			||||||
	SharedManager.Add(this.label, time.Since(this.startTime).Seconds()*1000)
 | 
						SharedManager.Add(this.label, time.Since(this.startTime).Seconds()*1000)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,20 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRawTicker(t *testing.T) {
 | 
				
			||||||
 | 
						var ticker = time.NewTicker(2 * time.Second)
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for range ticker.C {
 | 
				
			||||||
 | 
								t.Log("tick")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							t.Log("stop")
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						time.Sleep(6 * time.Second)
 | 
				
			||||||
 | 
						ticker.Stop()
 | 
				
			||||||
 | 
						time.Sleep(1 * time.Second)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestTicker(t *testing.T) {
 | 
					func TestTicker(t *testing.T) {
 | 
				
			||||||
	ticker := NewTicker(3 * time.Second)
 | 
						ticker := NewTicker(3 * time.Second)
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user