Go语言 用数据库实现分布式锁

业务场景:该服务需要每隔固定时间同步一次数据库,服务部署在多台机器,我们只希望同时有一台机器在做同步操作。
实现:包含scheduler.go和util.go两个文件。scheduler.go跑线程,util.go通过一个锁表实现分布式。

//scheduler.go

type Scheduler struct {
}

var scheduler *Scheduler
var scheduler_once sync.Once

func GetSchedulerInstance() *Scheduler {
	scheduler_once.Do(func() {
		scheduler = &Scheduler{}
	})
	return scheduler
}

func (scheduler *Scheduler) Run() {
	go scheduler.TaskRunner()
}

func (scheduler *Scheduler) TaskRunner() (int, error) {
	var util Util
	var conf *BaseConf
	conf = GetConfInstance()
	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s",
		conf.GetDBConf()[0].User,
		conf.GetDBConf()[0].Password,
		conf.GetDBConf()[0].Host,
		conf.GetDBConf()[0].Port,
		conf.GetDBConf()[0].Database,
		conf.GetDBConf()[0].Charset))
	if err != nil {
		tars.TLOG.Error("Open database0 error: ", err)
		return -1, err
	}
	defer db.Close()

	for {
		tx, err := db.Begin()
		if err != nil {
			tars.TLOG.Error(fmt.Sprintf("loop EXPAND_FIRST_LOOPER tx error: %s", err))
			time.Sleep(60 * time.Second)
			continue
		}
		if !util.GetLockFromLooperLocks("LOCK_DATA_LOOPER", "UPDATE_TASK_STATUS_ACCESS", tx) {
			util.LooperLocksCommitOrRollBack(tx, -1)
			time.Sleep(60 * time.Second)
			continue
		}
		
		//业务逻辑代码
		//业务逻辑代码
		//业务逻辑代码
		
		if  发生错误{
			tars.TLOG.Error("failed")
			util.LooperLocksCommitOrRollBack(tx, -1)
			return 0, nil
		}
		util.LooperLocksCommitOrRollBack(tx, 0)
		time.Sleep(300 * time.Second)//定时间隔
	}
	return 0, nil
}
//util,go

func (util *Util) GetLockFromLooperLocks(loop_name string, lock_name string, tx *sql.Tx) bool {
	//TODO) tx null point
	if tx == nil {
		tars.TLOG.Error("tx is nil")
		return false
	}
	//TODO mdb not support nowait|wait|skip locked
	sql_str := "select * from running_loop_locks where loop_name=? and lock_name=? for update"
	results, err := tx.Query(sql_str, loop_name, lock_name)
	defer results.Close()
	if err != nil {
		tars.TLOG.Error(fmt.Sprintf("loop_name: %s,get lock error: %s", loop_name, err))
		return false
	}
	for results.Next() {
	}
	return true
}

func (util *Util) LooperLocksCommitOrRollBack(tx *sql.Tx, ret int) (int, error) {
	if tx == nil {
		return 1, nil
	} else if 0 == ret {
		tx.Commit()
	} else {
		tx.Rollback()
	}
	return 0, nil
}

这里的分布式锁利用了for update的特性:当select * from … for update语句没有commit或rollback时,其他进程(同机器或不同机器)会阻塞于这条语句,因此保证了同一时间只有一台机器上的一个进程进行业务逻辑的运行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章