0
点赞
收藏
分享

微信扫一扫

Golang基于Mysql分布式锁实现集群主备

源码之路 2022-02-18 阅读 17

背景

集群中如果需要主备,可以基于Redis、zk的分布式锁等实现,本文将介绍如何利用Mysql分布式锁进行实现。

原理

  • 数据库中包含数据字段(此处为Master的主机名)、版本号和上一次更新时间。
  • Master不断上传自己的心跳,即刷新数据库中的"更新时间"。
  • 上一次更新时间超过了一定时间,则认为Master已Down,则可以抢Master。
  • 抢Master和更新心跳时,版本号+1,要判断版本号是否与上一次读取的数据相同。如果相同,则修改成功。如果不相同,则说明Master已经被其他主机抢走。

数据库建表

  • master存放主机名
CREATE TABLE `host_master` (
  `id` int NOT NULL AUTO_INCREMENT,
  `master` varchar(64) NOT NULL COMMENT '主机名',
  `version` int COMMENT '版本号',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存数据时间,自动生成',
  PRIMARY KEY (`id`)
)
 ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into host_master(master,version) value('',0);	//插入一条空数据

Golang实现集群主备

package main

import (
	"errors"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"os"
	"time"
)
var (
	DB *gorm.DB
	curHost = "2"
	healthTime float64	= 10	//上传心跳的周期
	healthTimeout float64 = 30	//健康检查过期时间
)
type HostMaster struct {
	ID         int64     `gorm:"column:id"`
	Master     string    `gorm:"column:master"`      //  主机名
	Version    int64     `gorm:"column:version"`     //  版本号
	UpdateTime *time.Time `gorm:"column:update_time"` //  保存数据时间,自动生成
}
//初始化数据库
func InitDB()error{
	var err error
	DB, err = gorm.Open("mysql", "root:123456@(192.168.191.128:3306)/test?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		return err
	}
	DB.SingularTable(true)
	return nil
}
//获取Master的信息
func GetMasterInfo()(HostMaster,error){
	var hostMasters []HostMaster
	ret := DB.Find(&hostMasters)
	if ret.Error!=nil{
		return HostMaster{},ret.Error
	}
	if ret.RowsAffected==0 || ret.RowsAffected>1{
		return HostMaster{},errors.New(fmt.Sprintf("HostMaster表中的条目为%d",ret.RowsAffected))
	}
	return hostMasters[0],nil
}
//抢Master与更新心跳
func GrabMaster()error{
	//获取Master的信息
	hostMaster,err := GetMasterInfo()
	if err!=nil{
		return err
	}
	//当前主机为Master则更新心跳.或Master已down则抢Master
	if hostMaster.Master==curHost || time.Now().Sub(*hostMaster.UpdateTime).Seconds()>healthTimeout{
		ret := DB.Model(&HostMaster{}).Where("version = ?",hostMaster.Version).Updates(map[string]interface{}{"master":curHost,"version":hostMaster.Version+1})
		if ret.Error!=nil{
			return errors.New("修改失败: "+ret.Error.Error())
		}
		if ret.RowsAffected==0{
			return nil
		}else{
			if hostMaster.Master==curHost{
				fmt.Println(curHost+"更新了心跳")
			}else{
				fmt.Println(curHost+"抢Master成功")
			}
		}
	}
	return nil
}
func main() {
	//初始化数据库
	err := InitDB()
	if err!=nil{
		fmt.Println(err)
		os.Exit(1)
	}
	//周期性更新心跳和抢Master
	go func(){
		for{
			err := GrabMaster()
			if err!=nil{
				fmt.Println(err)
			}
			time.Sleep(10*time.Second)
		}
	}()
	select {}
}




郭少

举报

相关推荐

0 条评论