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時,其他進程(同機器或不同機器)會阻塞於這條語句,因此保證了同一時間只有一臺機器上的一個進程進行業務邏輯的運行。

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