github.com/TeaOSLab/EdgeNode@v1.3.8/internal/caches/list_memory.go (about)

     1  package caches
     2  
     3  import (
     4  	"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
     5  	"github.com/iwind/TeaGo/logs"
     6  	"net"
     7  	"net/url"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  	"sync/atomic"
    12  	"testing"
    13  )
    14  
    15  // MemoryList 内存缓存列表管理
    16  type MemoryList struct {
    17  	count int64
    18  
    19  	itemMaps map[string]map[string]*Item // prefix => { hash => item }
    20  
    21  	prefixes []string
    22  	locker   sync.RWMutex
    23  	onAdd    func(item *Item)
    24  	onRemove func(item *Item)
    25  
    26  	purgeIndex int
    27  }
    28  
    29  func NewMemoryList() ListInterface {
    30  	return &MemoryList{
    31  		itemMaps: map[string]map[string]*Item{},
    32  	}
    33  }
    34  
    35  func (this *MemoryList) Init() error {
    36  	this.prefixes = []string{"000"}
    37  	for i := 100; i <= 999; i++ {
    38  		this.prefixes = append(this.prefixes, strconv.Itoa(i))
    39  	}
    40  
    41  	for _, prefix := range this.prefixes {
    42  		this.itemMaps[prefix] = map[string]*Item{}
    43  	}
    44  
    45  	return nil
    46  }
    47  
    48  func (this *MemoryList) Reset() error {
    49  	this.locker.Lock()
    50  	for key := range this.itemMaps {
    51  		this.itemMaps[key] = map[string]*Item{}
    52  	}
    53  	this.locker.Unlock()
    54  
    55  	atomic.StoreInt64(&this.count, 0)
    56  
    57  	return nil
    58  }
    59  
    60  func (this *MemoryList) Add(hash string, item *Item) error {
    61  	this.locker.Lock()
    62  
    63  	prefix := this.prefix(hash)
    64  	itemMap, ok := this.itemMaps[prefix]
    65  	if !ok {
    66  		itemMap = map[string]*Item{}
    67  		this.itemMaps[prefix] = itemMap
    68  	}
    69  
    70  	// 先删除,为了可以正确触发统计
    71  	oldItem, ok := itemMap[hash]
    72  	if ok {
    73  		// 回调
    74  		if this.onRemove != nil {
    75  			this.onRemove(oldItem)
    76  		}
    77  	} else {
    78  		atomic.AddInt64(&this.count, 1)
    79  	}
    80  
    81  	// 添加
    82  	if this.onAdd != nil {
    83  		this.onAdd(item)
    84  	}
    85  
    86  	itemMap[hash] = item
    87  
    88  	this.locker.Unlock()
    89  	return nil
    90  }
    91  
    92  func (this *MemoryList) Exist(hash string) (bool, int64, error) {
    93  	this.locker.RLock()
    94  	defer this.locker.RUnlock()
    95  
    96  	prefix := this.prefix(hash)
    97  	itemMap, ok := this.itemMaps[prefix]
    98  	if !ok {
    99  		return false, -1, nil
   100  	}
   101  	item, ok := itemMap[hash]
   102  	if !ok {
   103  		return false, -1, nil
   104  	}
   105  
   106  	return !item.IsExpired(), -1, nil
   107  }
   108  
   109  // CleanPrefix 根据前缀进行清除
   110  func (this *MemoryList) CleanPrefix(prefix string) error {
   111  	this.locker.RLock()
   112  	defer this.locker.RUnlock()
   113  
   114  	// TODO 需要优化性能,支持千万级数据低于1s的处理速度
   115  	for _, itemMap := range this.itemMaps {
   116  		for _, item := range itemMap {
   117  			if strings.HasPrefix(item.Key, prefix) {
   118  				item.ExpiresAt = 0
   119  			}
   120  		}
   121  	}
   122  	return nil
   123  }
   124  
   125  // CleanMatchKey 清理通配符匹配的缓存数据,类似于 https://*.example.com/hello
   126  func (this *MemoryList) CleanMatchKey(key string) error {
   127  	if strings.Contains(key, SuffixAll) {
   128  		return nil
   129  	}
   130  
   131  	u, err := url.Parse(key)
   132  	if err != nil {
   133  		return nil
   134  	}
   135  
   136  	var host = u.Host
   137  	hostPart, _, err := net.SplitHostPort(host)
   138  	if err == nil && len(hostPart) > 0 {
   139  		host = hostPart
   140  	}
   141  
   142  	if len(host) == 0 {
   143  		return nil
   144  	}
   145  	var requestURI = u.RequestURI()
   146  
   147  	this.locker.RLock()
   148  	defer this.locker.RUnlock()
   149  
   150  	// TODO 需要优化性能,支持千万级数据低于1s的处理速度
   151  	for _, itemMap := range this.itemMaps {
   152  		for _, item := range itemMap {
   153  			if configutils.MatchDomain(host, item.Host) {
   154  				var itemRequestURI = item.RequestURI()
   155  				if itemRequestURI == requestURI || strings.HasPrefix(itemRequestURI, requestURI+SuffixAll) {
   156  					item.ExpiresAt = 0
   157  				}
   158  			}
   159  		}
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  // CleanMatchPrefix 清理通配符匹配的缓存数据,类似于 https://*.example.com/prefix/
   166  func (this *MemoryList) CleanMatchPrefix(prefix string) error {
   167  	u, err := url.Parse(prefix)
   168  	if err != nil {
   169  		return nil
   170  	}
   171  
   172  	var host = u.Host
   173  	hostPart, _, err := net.SplitHostPort(host)
   174  	if err == nil && len(hostPart) > 0 {
   175  		host = hostPart
   176  	}
   177  	if len(host) == 0 {
   178  		return nil
   179  	}
   180  	var requestURI = u.RequestURI()
   181  	var isRootPath = requestURI == "/"
   182  
   183  	this.locker.RLock()
   184  	defer this.locker.RUnlock()
   185  
   186  	// TODO 需要优化性能,支持千万级数据低于1s的处理速度
   187  	for _, itemMap := range this.itemMaps {
   188  		for _, item := range itemMap {
   189  			if configutils.MatchDomain(host, item.Host) {
   190  				var itemRequestURI = item.RequestURI()
   191  				if isRootPath || strings.HasPrefix(itemRequestURI, requestURI) {
   192  					item.ExpiresAt = 0
   193  				}
   194  			}
   195  		}
   196  	}
   197  
   198  	return nil
   199  }
   200  
   201  func (this *MemoryList) Remove(hash string) error {
   202  	this.locker.Lock()
   203  
   204  	itemMap, ok := this.itemMaps[this.prefix(hash)]
   205  	if !ok {
   206  		this.locker.Unlock()
   207  		return nil
   208  	}
   209  
   210  	item, ok := itemMap[hash]
   211  	if ok {
   212  		if this.onRemove != nil {
   213  			this.onRemove(item)
   214  		}
   215  
   216  		atomic.AddInt64(&this.count, -1)
   217  		delete(itemMap, hash)
   218  	}
   219  
   220  	this.locker.Unlock()
   221  	return nil
   222  }
   223  
   224  // Purge 清理过期的缓存
   225  // count 每次遍历的最大数量,控制此数字可以保证每次清理的时候不用花太多时间
   226  // callback 每次发现过期key的调用
   227  func (this *MemoryList) Purge(count int, callback func(hash string) error) (int, error) {
   228  	this.locker.Lock()
   229  	var deletedHashList = []string{}
   230  
   231  	if this.purgeIndex >= len(this.prefixes) {
   232  		this.purgeIndex = 0
   233  	}
   234  	var prefix = this.prefixes[this.purgeIndex]
   235  
   236  	this.purgeIndex++
   237  
   238  	itemMap, ok := this.itemMaps[prefix]
   239  	if !ok {
   240  		this.locker.Unlock()
   241  		return 0, nil
   242  	}
   243  	var countFound = 0
   244  	for hash, item := range itemMap {
   245  		if count <= 0 {
   246  			break
   247  		}
   248  
   249  		if item.IsExpired() {
   250  			if this.onRemove != nil {
   251  				this.onRemove(item)
   252  			}
   253  
   254  			atomic.AddInt64(&this.count, -1)
   255  			delete(itemMap, hash)
   256  			deletedHashList = append(deletedHashList, hash)
   257  
   258  			countFound++
   259  		}
   260  
   261  		count--
   262  	}
   263  	this.locker.Unlock()
   264  
   265  	// 执行外部操作
   266  	for _, hash := range deletedHashList {
   267  		if callback != nil {
   268  			err := callback(hash)
   269  			if err != nil {
   270  				return 0, err
   271  			}
   272  		}
   273  	}
   274  	return countFound, nil
   275  }
   276  
   277  func (this *MemoryList) PurgeLFU(count int, callback func(hash string) error) error {
   278  	if count <= 0 {
   279  		return nil
   280  	}
   281  
   282  	var deletedHashList = []string{}
   283  
   284  	var week = currentWeek()
   285  	var round = 0
   286  
   287  	this.locker.Lock()
   288  
   289  Loop:
   290  	for {
   291  		var found = false
   292  		round++
   293  		for _, itemMap := range this.itemMaps {
   294  			for hash, item := range itemMap {
   295  				found = true
   296  
   297  				if week-item.Week <= 1 /** 最近有在使用 **/ && round <= 3 /** 查找轮数过多还不满足数量要求的就不再限制 **/ {
   298  					continue
   299  				}
   300  
   301  				if this.onRemove != nil {
   302  					this.onRemove(item)
   303  				}
   304  
   305  				atomic.AddInt64(&this.count, -1)
   306  				delete(itemMap, hash)
   307  				deletedHashList = append(deletedHashList, hash)
   308  
   309  				count--
   310  				if count <= 0 {
   311  					break Loop
   312  				}
   313  
   314  				break
   315  			}
   316  		}
   317  		if !found {
   318  			break
   319  		}
   320  	}
   321  
   322  	this.locker.Unlock()
   323  
   324  	// 执行外部操作
   325  	for _, hash := range deletedHashList {
   326  		if callback != nil {
   327  			err := callback(hash)
   328  			if err != nil {
   329  				return err
   330  			}
   331  		}
   332  	}
   333  
   334  	return nil
   335  }
   336  
   337  func (this *MemoryList) CleanAll() error {
   338  	return this.Reset()
   339  }
   340  
   341  func (this *MemoryList) Stat(check func(hash string) bool) (*Stat, error) {
   342  	this.locker.RLock()
   343  	defer this.locker.RUnlock()
   344  
   345  	result := &Stat{
   346  		Count: 0,
   347  		Size:  0,
   348  	}
   349  	for _, itemMap := range this.itemMaps {
   350  		for hash, item := range itemMap {
   351  			if !item.IsExpired() {
   352  				// 检查文件是否存在、内容是否正确等
   353  				if check != nil && check(hash) {
   354  					result.Count++
   355  					result.ValueSize += item.Size()
   356  					result.Size += item.TotalSize()
   357  				}
   358  			}
   359  		}
   360  	}
   361  	return result, nil
   362  }
   363  
   364  // Count 总数量
   365  func (this *MemoryList) Count() (int64, error) {
   366  	var count = atomic.LoadInt64(&this.count)
   367  	return count, nil
   368  }
   369  
   370  // OnAdd 添加事件
   371  func (this *MemoryList) OnAdd(f func(item *Item)) {
   372  	this.onAdd = f
   373  }
   374  
   375  // OnRemove 删除事件
   376  func (this *MemoryList) OnRemove(f func(item *Item)) {
   377  	this.onRemove = f
   378  }
   379  
   380  func (this *MemoryList) Close() error {
   381  	return nil
   382  }
   383  
   384  // IncreaseHit 增加点击量
   385  func (this *MemoryList) IncreaseHit(hash string) error {
   386  	this.locker.Lock()
   387  
   388  	itemMap, ok := this.itemMaps[this.prefix(hash)]
   389  	if !ok {
   390  		this.locker.Unlock()
   391  		return nil
   392  	}
   393  
   394  	item, ok := itemMap[hash]
   395  	if ok {
   396  		item.Week = currentWeek()
   397  	}
   398  
   399  	this.locker.Unlock()
   400  	return nil
   401  }
   402  
   403  func (this *MemoryList) Prefixes() []string {
   404  	return this.prefixes
   405  }
   406  
   407  func (this *MemoryList) ItemMaps() map[string]map[string]*Item {
   408  	return this.itemMaps
   409  }
   410  
   411  func (this *MemoryList) PurgeIndex() int {
   412  	return this.purgeIndex
   413  }
   414  
   415  func (this *MemoryList) Print(t *testing.T) {
   416  	this.locker.Lock()
   417  	for _, itemMap := range this.itemMaps {
   418  		if len(itemMap) > 0 {
   419  			logs.PrintAsJSON(itemMap, t)
   420  		}
   421  	}
   422  	this.locker.Unlock()
   423  }
   424  
   425  func (this *MemoryList) prefix(hash string) string {
   426  	var prefix string
   427  	if len(hash) > 3 {
   428  		prefix = hash[:3]
   429  	} else {
   430  		prefix = hash
   431  	}
   432  	_, ok := this.itemMaps[prefix]
   433  	if !ok {
   434  		prefix = "000"
   435  	}
   436  	return prefix
   437  }