業務場景:該服務需要每隔固定時間同步一次數據庫,服務部署在多臺機器,我們只希望同時有一臺機器在做同步操作。
實現:包含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時,其他進程(同機器或不同機器)會阻塞於這條語句,因此保證了同一時間只有一臺機器上的一個進程進行業務邏輯的運行。