优化策略
This commit is contained in:
+180
-89
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
@@ -14,83 +14,120 @@ import (
|
||||
// 多版本场景判断当前是否最新版本
|
||||
|
||||
type Priority struct {
|
||||
ctx context.Context
|
||||
priority int64 // 优先级
|
||||
redis redis.UniversalClient
|
||||
redisKey string
|
||||
logger logger.Logger
|
||||
expireTime time.Duration
|
||||
updateInterval time.Duration // 更新间隔
|
||||
deadTime *int64 // 缓存时间戳,单位秒
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
priority int64 // 优先级
|
||||
redis redis.UniversalClient
|
||||
redisKey string
|
||||
logger logger.Logger
|
||||
expireTime time.Duration
|
||||
|
||||
setInterval time.Duration // 尝试set的间隔
|
||||
getInterval time.Duration // 尝试get的间隔
|
||||
|
||||
wg sync.WaitGroup
|
||||
|
||||
isLatest bool
|
||||
latestMux sync.RWMutex
|
||||
}
|
||||
|
||||
func InitPriority(ctx context.Context, re redis.UniversalClient, keyPrefix string, priority int64, opts ...Option) *Priority {
|
||||
conf := newOptions(opts...)
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
pro := &Priority{
|
||||
ctx: ctx,
|
||||
priority: priority,
|
||||
redis: re,
|
||||
logger: conf.logger,
|
||||
redisKey: "timer:priority_" + keyPrefix,
|
||||
expireTime: conf.expireTime,
|
||||
updateInterval: conf.updateInterval,
|
||||
deadTime: new(int64),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
priority: priority,
|
||||
redis: re,
|
||||
logger: conf.logger,
|
||||
redisKey: "timer:priority_" + keyPrefix,
|
||||
expireTime: conf.expireTime,
|
||||
setInterval: conf.updateInterval,
|
||||
getInterval: conf.getInterval,
|
||||
}
|
||||
|
||||
// 更新间隔
|
||||
ut := time.NewTicker(conf.updateInterval)
|
||||
go func(ctx context.Context) {
|
||||
pro.setPriority()
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case <-ut.C:
|
||||
pro.setPriority()
|
||||
case <-ctx.Done():
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
}(ctx)
|
||||
pro.startDaemon()
|
||||
|
||||
return pro
|
||||
}
|
||||
|
||||
func (l *Priority) IsLatest(ctx context.Context) (b bool) {
|
||||
// defer func() {
|
||||
// l.logger.Infof(l.ctx, "当前优先级:%d bool:%v", l.priority, b)
|
||||
// }()
|
||||
|
||||
// 加缓存
|
||||
if atomic.LoadInt64(l.deadTime) > time.Now().Unix() {
|
||||
return true
|
||||
func (p *Priority) Close() {
|
||||
if p.cancel != nil {
|
||||
p.cancel()
|
||||
}
|
||||
|
||||
str, err := l.redis.Get(l.ctx, l.redisKey).Result()
|
||||
|
||||
if err != nil {
|
||||
if err == redis.Nil && l.priority == 0 {
|
||||
return true
|
||||
}
|
||||
l.logger.Errorf(l.ctx, "获取全局优先级失败:%s", err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
strPriority, err := strconv.ParseInt(str, 10, 64)
|
||||
if err != nil {
|
||||
l.logger.Errorf(l.ctx, "全局优先级转换失败:%s", err.Error())
|
||||
return false
|
||||
}
|
||||
if l.priority >= strPriority {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
p.wg.Wait()
|
||||
}
|
||||
|
||||
func (l *Priority) setPriority() bool {
|
||||
// 守护进程
|
||||
func (l *Priority) startDaemon() {
|
||||
// 启动更新缓存
|
||||
l.wg.Add(1)
|
||||
go l.runUpdateLoop()
|
||||
|
||||
// redis lua脚本
|
||||
// 如果redis的可以不存在则设置值,如果存在且redis内的值比当前大则不处理,如果存在redis内的值比当前小或等于则更新值且更新ttl
|
||||
l.wg.Add(1)
|
||||
go l.getLatestLoop()
|
||||
|
||||
}
|
||||
|
||||
func (p *Priority) runUpdateLoop() {
|
||||
defer p.wg.Done()
|
||||
|
||||
// 立即尝试设置一次优先级
|
||||
if _, err := p.setPriority(); err != nil {
|
||||
p.logger.Warnf(p.ctx, "Initial priority set failed: %v", err)
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(p.setInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if _, err := p.setPriority(); err != nil {
|
||||
p.logger.Warnf(p.ctx, "Priority update failed: %v", err)
|
||||
}
|
||||
case <-p.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Priority) getLatestLoop() {
|
||||
|
||||
defer l.wg.Done()
|
||||
|
||||
if err := l.getLatest(); err != nil {
|
||||
l.logger.Errorf(l.ctx, "Priority update failed: %v", err)
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(l.getInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := l.getLatest(); err != nil {
|
||||
l.logger.Errorf(l.ctx, "Priority update failed: %v", err)
|
||||
}
|
||||
case <-l.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
func (p *Priority) IsLatest(ctx context.Context) bool {
|
||||
p.latestMux.RLock()
|
||||
defer p.latestMux.RUnlock()
|
||||
|
||||
return p.isLatest
|
||||
}
|
||||
|
||||
func (p *Priority) setPriority() (string, error) {
|
||||
script := `
|
||||
-- KEYS[1] 是全局优先级的key
|
||||
local priorityKey = KEYS[1]
|
||||
@@ -112,47 +149,101 @@ func (l *Priority) setPriority() bool {
|
||||
if not currentPriority then
|
||||
-- 如果当前优先级不存在,则设置新优先级并设置TTL
|
||||
redis.call('set', priorityKey, priority, 'ex', expireTime)
|
||||
return { "SET", expireTime }
|
||||
return { "SET" }
|
||||
elseif currentPriorityNum < newPriorityNum then
|
||||
-- 如果当前优先级小于新优先级,则更新优先级并更新TTL
|
||||
redis.call('set', priorityKey, priority, 'ex', expireTime)
|
||||
return { "RESET", expireTime }
|
||||
return { "RESET" }
|
||||
elseif currentPriorityNum == newPriorityNum then
|
||||
-- 优先级相同则更新TTL
|
||||
redis.call('expire', priorityKey, expireTime)
|
||||
return { "UPDATE", expireTime }
|
||||
return { "UPDATE" }
|
||||
else
|
||||
-- 如果当前优先级大于新优先级,则不更新
|
||||
return { "NOAUCH", '0' }
|
||||
return { "NOAUCH" }
|
||||
end
|
||||
`
|
||||
priority := fmt.Sprintf("%d", l.priority)
|
||||
|
||||
res, err := l.redis.Eval(l.ctx, script, []string{l.redisKey}, priority, l.expireTime.Seconds()).Result()
|
||||
newPriorityStr := strconv.FormatInt(p.priority, 10)
|
||||
|
||||
result, err := p.redis.Eval(p.ctx, script, []string{p.redisKey}, newPriorityStr, p.expireTime.Seconds()).Result()
|
||||
// p.logger.Infof(p.ctx, "Priority update result:%+v err:%+v", result, err)
|
||||
if err != nil {
|
||||
l.logger.Errorf(l.ctx, "设置全局优先级失败:%s", err.Error())
|
||||
return false
|
||||
p.logger.Errorf(p.ctx, "Priority update err:%s", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
l.logger.Infof(l.ctx, "设置全局优先级返回值:%+v", res)
|
||||
|
||||
// 处理返回值,包含操作结果和 TTL
|
||||
resultArray := res.([]interface{})
|
||||
if len(resultArray) < 2 {
|
||||
l.logger.Errorf(l.ctx, "设置全局优先级失败: 返回值格式不正确")
|
||||
return false
|
||||
// 解析结果
|
||||
if resultMap, ok := result.([]interface{}); ok && len(resultMap) == 1 {
|
||||
resultStr := resultMap[0].(string)
|
||||
return resultStr, nil
|
||||
}
|
||||
operationResult := resultArray[0].(string)
|
||||
ttl := resultArray[1].(string)
|
||||
|
||||
if operationResult == "SET" || operationResult == "UPDATE" {
|
||||
l.logger.Infof(l.ctx, "设置全局优先级成功:%s", priority)
|
||||
|
||||
atomic.StoreInt64(l.deadTime, time.Now().Add(l.updateInterval).Unix())
|
||||
|
||||
return true
|
||||
}
|
||||
_ = ttl
|
||||
l.logger.Infof(l.ctx, "设置全局优先级未更新:%s", priority)
|
||||
return false
|
||||
return "", fmt.Errorf("script error: %v", result)
|
||||
}
|
||||
|
||||
func (l *Priority) getLatest() error {
|
||||
// 查询Redis获取当前最高优先级
|
||||
currentPriority, err := l.getCurrentPriority()
|
||||
|
||||
l.logger.Infof(l.ctx, "Priority getLatest currentPriority:%d l.priority:%d err:%+v", currentPriority, l.priority, err)
|
||||
|
||||
if err != nil {
|
||||
l.logger.Errorf(l.ctx, "Priority getLatest getCurrentPriority err:%s", err.Error())
|
||||
return err
|
||||
}
|
||||
if currentPriority > l.priority {
|
||||
// 当前不是最新的
|
||||
l.latestMux.Lock()
|
||||
l.isLatest = false
|
||||
l.latestMux.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
l.latestMux.Lock()
|
||||
l.isLatest = true
|
||||
l.latestMux.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Priority) getCurrentPriority() (int64, error) {
|
||||
result, err := p.redis.Get(p.ctx, p.redisKey).Result()
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
// Key不存在,返回0作为默认值
|
||||
return 0, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
priority, err := strconv.ParseInt(result, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return priority, nil
|
||||
}
|
||||
|
||||
// ForceRefresh 强制刷新优先级,用于紧急情况
|
||||
func (p *Priority) ForceRefresh() error {
|
||||
|
||||
_, err := p.setPriority()
|
||||
if err != nil {
|
||||
p.logger.Errorf(p.ctx, "Priority ForceRefresh setPriority err:%s", err.Error())
|
||||
return err
|
||||
}
|
||||
err = p.getLatest()
|
||||
if err != nil {
|
||||
p.logger.Errorf(p.ctx, "Priority ForceRefresh getLatest err:%s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCurrentMaxPriority 获取当前系统中的最大优先级
|
||||
func (p *Priority) GetCurrentMaxPriority(ctx context.Context) (int64, error) {
|
||||
return p.getCurrentPriority()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user