github.com/songzhibin97/gkit@v1.2.13/window/leap_array.go (about) 1 package window 2 3 import ( 4 "errors" 5 "runtime" 6 "sync/atomic" 7 8 "github.com/songzhibin97/gkit/internal/clock" 9 "github.com/songzhibin97/gkit/internal/sys/mutex" 10 ) 11 12 var ( 13 ErrBucketBuilderIsNil = errors.New("invalid parameters, Builder is nil") 14 ErrWindowNotSegmentation = errors.New("invalid parameters,window is not segmentation") 15 ErrTimeBehindStart = errors.New("time already behind bucketStart") 16 ) 17 18 // TimeChick 自定义校验 时间戳 函数 19 type TimeChick func(uint64) bool 20 21 // LeapArray 基于 Bucket 实现的 leap array 22 // 例如: bucketSize == 200ms,intervalSize == 1000ms,所以n = 5 23 // 假设当前是 时间是888ms 构建下图 24 // B0 B1 B2 B3 B4 25 // |_______|_______|_______|_______|_______| 26 // 1000 1200 1400 1600 800 (1000) 27 // ^ 28 // time=888 29 type LeapArray struct { 30 // lock: 互斥自旋锁 31 mu mutex.Mutex 32 33 // bucketSize: 桶的长度 34 bucketSize uint64 35 36 // intervalSize: array间隔大小 37 intervalSize uint64 38 39 // n: 代表桶的个数 40 n uint64 41 42 // array: 底层数组 43 array *AtomicArray 44 } 45 46 // getTimeIndex 获取当前时间 命中的index索引 47 func (s *LeapArray) getTimeIndex(now uint64) uint64 { 48 id := now / s.bucketSize 49 return id % s.array.length 50 } 51 52 // getBucketOfTime 根据时间戳获取到对应的桶 53 func (s *LeapArray) getBucketOfTime(now uint64, builder BucketBuilder) (*Bucket, error) { 54 index := s.getTimeIndex(now) 55 start := calculateStartTime(now, s.bucketSize) 56 57 for { 58 // 获取当前的 bucket 59 old := s.array.getBucket(index) 60 if old == nil { 61 // 懒加载 62 b := &Bucket{ 63 Start: start, 64 Value: atomic.Value{}, 65 } 66 b.Value.Store(builder.NewEmptyBucket()) 67 if s.array.compareAndSwap(index, nil, b) { 68 return b, nil 69 } 70 runtime.Gosched() 71 } else if start == atomic.LoadUint64(&old.Start) { 72 // 在时间范围内 73 return old, nil 74 } else if start > atomic.LoadUint64(&old.Start) { 75 // 命中下一个周期 76 77 // 尝试获取锁 78 if s.mu.TryLock() { 79 // 重置 80 old = builder.Reset(old, start) 81 s.mu.Unlock() 82 return old, nil 83 } 84 runtime.Gosched() 85 } else if start < atomic.LoadUint64(&old.Start) { 86 if s.n == 1 { 87 // 如果在跳转数组中 n == 1,则在并发情况下,这种情况是可能的 88 return old, nil 89 } 90 return nil, ErrTimeBehindStart 91 } 92 } 93 } 94 95 // GetBucket 获取桶,封装 getBucketOfTime 96 func (s *LeapArray) GetBucket(builder BucketBuilder) (*Bucket, error) { 97 return s.getBucketOfTime(clock.GetTimeMillis(), builder) 98 } 99 100 // isDisable 判断当前桶是否被弃用 101 func (s *LeapArray) isDisable(now uint64, b *Bucket) bool { 102 ws := atomic.LoadUint64(&b.Start) 103 return (now - ws) > s.intervalSize 104 } 105 106 // getValueOfTime 通过 now 为基点 获取array所有桶 107 func (s *LeapArray) getValueOfTime(now uint64) []*Bucket { 108 ret := make([]*Bucket, 0, s.array.length) 109 for i := (uint64)(0); i < s.array.length; i++ { 110 b := s.array.getBucket(i) 111 if b == nil || s.isDisable(now, b) { 112 continue 113 } 114 ret = append(ret, b) 115 } 116 return ret 117 } 118 119 // Values getValueOfTime 封装 120 func (s *LeapArray) Values() []*Bucket { 121 return s.getValueOfTime(clock.GetTimeMillis()) 122 } 123 124 // ValuesChick 加入自定义 TimeChick 过滤value 125 func (s *LeapArray) ValuesChick(now uint64, chick TimeChick) []*Bucket { 126 ret := make([]*Bucket, 0, s.array.length) 127 for i := (uint64)(0); i < s.array.length; i++ { 128 b := s.array.getBucket(i) 129 if b == nil || s.isDisable(now, b) || !chick(atomic.LoadUint64(&b.Start)) { 130 continue 131 } 132 ret = append(ret, b) 133 } 134 return ret 135 } 136 137 // NewLeapArray 初始化 leap array 138 func NewLeapArray(n uint64, intervalSize uint64, builder BucketBuilder) (*LeapArray, error) { 139 if builder == nil { 140 return nil, ErrBucketBuilderIsNil 141 } 142 if intervalSize%n != 0 { 143 return nil, ErrWindowNotSegmentation 144 } 145 bucketSize := intervalSize / n 146 return &LeapArray{ 147 bucketSize: bucketSize, 148 intervalSize: intervalSize, 149 n: n, 150 array: NewAtomicArray(n, bucketSize, builder), 151 }, nil 152 }