背景
集群中如果需要主备,可以基于Redis、zk的分布式锁等实现,本文将介绍如何利用Mysql分布式锁进行实现。
原理
- 数据库中包含数据字段(此处为Master的主机名)、版本号和上一次更新时间。
- Master不断上传自己的心跳,即刷新数据库中的"更新时间"。
- 上一次更新时间超过了一定时间,则认为Master已Down,则可以抢Master。
- 抢Master和更新心跳时,版本号+1,要判断版本号是否与上一次读取的数据相同。如果相同,则修改成功。如果不相同,则说明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 {}
- }
复制代码
郭少
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |