go redis使用可重入锁支持选主

使用redis实现多实例选主功能:

为了保证稳定性,后端会部署多实例,在读的场景下其实是不需要选主的。但是也有需要单实例运行的人文,这就需要选主。


问题描述

像主从模式在创建是声明实例master实例直接处理写请求,但是单主写会存在单机风险。像redis就会启用哨兵模式,支持重新选主模式。哨兵模式为了保证一致性和高性能而建立的选主模式,对一些低频交互,低资源共享,低多用户并发的场景下,哨兵模式太重了。


原因分析:

在我们的场景下,资源共享需求较低,很多用户的资源是独占的,所以对用户写失效其实可以重试,没有很高的一致危险。使用一个有时效的互斥锁,可以实现选主功能,这个原理跟K8s选主的有点像,原理参照k8s选主机制

解决方案:

定义一个选主对象,选主锁使用redis时效互斥锁,只有得到该锁lock才能运行master程序OnLeading

type LeaderSelector struct {
	lock      *RedisExistLock
	OnLeading []func(context.Context)
	ctx       context.Context
	isLeader  bool
}

把需要在leader状态下运行的方法进行注册,该方法会在处于leader的时候被runOnLeading运行

func (l *LeaderSelector)RegisterFunc(f func(context.Context)) {
	l.OnLeading = append(l.OnLeading, f)
}
func (l *LeaderSelector) runOnLeading() {
	if !l.isLeader{
		return 
	}
	for _, f := range l.OnLeading {
		select {
		case <-l.ctx.Done():
		default:
			go f(l.ctx)
		}
	}
}

尝试使用TryLock来获取选主,要是选主失败,那就sleep到下个选主周期。
获取选主锁成功后,运行选主后运行runOnLeading,需要进行计时,超时就要返回

func (l *LeaderSelector) StartedLeading(waitt time.Duration) {
	l.isLeader = false
	if ok, err := l.lock.TryLock(); ok {
		l.isLeader = true
		defer func() { 
			l.isLeader = false 
		}()

		ctx, cancel := context.WithTimeout(n*time.Second)
		defer cancel()
		succ := make(chan bool)
		defer close(succ)
		go func() {
			l.runOnLeading()
			succ <- true
		}()
		select {
		case <-ctx.Done():
		case <-succ:
		}
		return
	} else if err != nil {
		time.Sleep(waitt + time.Second)
	}
}

初始化和启动选主功能

func InitLeaderSelector(ctx context.Context, f func(context.Context)) *LeaderSelector {
	return &LeaderSelector{
		lock:             NewRedisExistLock(),
		ctx:              ctx,
	}
}
func (l *LeaderSelector)RunLeaderSelector() {
	for {
		select {
		case <-l.ctx.Done():
			return
		default:
			l.StartedLeading(time.Second * n) // 周期选主
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值