hello,大家好呀,我是小楼。
前几天不是写了这篇文章《发现一个开源项目优化点,点进来就是你的了》嘛。
文章介绍了Sentinl的自适应缓存时间戳算法,从原理到实现都手把手解读了,而且还发现Sentinel-Go还未实现这个自适应算法,于是我就觉得,这简单啊,把Java代码翻译成Go不就可以混个PR?
甚至在文章初稿中把这个描述为:「有手就可以」,感觉不太妥当,后来被我删掉了。
过了几天,我想去看看有没有人看了我的文章真的去提了个PR,发现仍然是没有,心想,可能是大家太忙(懒)了吧。
于是准备自己来实现一遍,周末我拿出电脑试着写一下这段代码,结果被当头一棒敲醒,原来这代码不好写啊。
如何实现
先简单介绍一下我当时是如何实现的。
首先,定义了系统的四种状态:
const (
UNINITIALIZED = iota
IDLE
PREPARE
RUNNING
)
这里为了让代码更加贴近Go的习惯,用了iota
。
用了4种状态,第一个状态UNINITIALIZED
是Java版里没有的,因为Java在系统初始化时默认就启动了定时缓存时间戳线程。
但Go版本不是这样的,它有个开关,当开关开启时,会调用StartTimeTicker
来启动缓存时间戳的协程,所以当没有初始化时是需要直接返回系统时间戳,所以这里多了一个UNINITIALIZED
状态。
然后我们需要能够统计QPS的方法,这块直接抄Java的实现,由于不是重点,但又怕你不理解,所以直接贴一点代码,不想看可以往下划。
定义我们需要的BucketWrap:
type statistic struct {
reads uint64
writes uint64
}
func (s *statistic) NewEmptyBucket() interface{
} {
return statistic{
reads: 0,
writes: 0,
}
}
func (s *statistic) ResetBucketTo(bucket *base.BucketWrap, startTime uint64) *base.BucketWrap {
atomic.StoreUint64(&bucket.BucketStart, startTime)
bucket.Value.Store(statistic{
reads: 0,
writes: 0,
})
return bucket
}
获取当前的Bucket:
func currentCounter(now uint64) (*statistic, error) {
if statistics == nil {
return nil, fmt.Errorf("statistics is nil")
}
bk, err := statistics.CurrentBucketOfTime(now, bucketGenerator)
if err != nil {
return nil, err
}
if bk == nil {
return nil, fmt.Errorf("current bucket is nil")
}
v := bk.Value.Load()
if v