业务场景:该服务需要每隔固定时间同步一次数据库,服务部署在多台机器,我们只希望同时有一台机器在做同步操作。
实现:包含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时,其他进程(同机器或不同机器)会阻塞于这条语句,因此保证了同一时间只有一台机器上的一个进程进行业务逻辑的运行。