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

火影  金牌会员 | 2023-2-26 00:47:54 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 865|帖子 865|积分 2595

背景

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


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


  • master存放主机名
  1. CREATE TABLE `host_master` (
  2.   `id` int NOT NULL AUTO_INCREMENT,
  3.   `master` varchar(64) NOT NULL COMMENT '主机名',
  4.   `version` int COMMENT '版本号',
  5.   `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存数据时间,自动生成',
  6.   PRIMARY KEY (`id`)
  7. )
  8. ENGINE=InnoDB DEFAULT CHARSET=utf8;
  9. insert into host_master(master,version) value('',0);    //插入一条空数据
复制代码
Golang实现集群主备
  1. package main
  2. import (
  3.     "errors"
  4.     "fmt"
  5.     "github.com/jinzhu/gorm"
  6.     _ "github.com/jinzhu/gorm/dialects/mysql"
  7.     "os"
  8.     "time"
  9. )
  10. var (
  11.     DB *gorm.DB
  12.     curHost = "2"
  13.     healthTime float64    = 10    //上传心跳的周期
  14.     healthTimeout float64 = 30    //健康检查过期时间
  15. )
  16. type HostMaster struct {
  17.     ID         int64     `gorm:"column:id"`
  18.     Master     string    `gorm:"column:master"`      //  主机名
  19.     Version    int64     `gorm:"column:version"`     //  版本号
  20.     UpdateTime *time.Time `gorm:"column:update_time"` //  保存数据时间,自动生成
  21. }
  22. //初始化数据库
  23. func InitDB()error{
  24.     var err error
  25.     DB, err = gorm.Open("mysql", "root:123456@(192.168.191.128:3306)/test?charset=utf8&parseTime=True&loc=Local")
  26.     if err != nil {
  27.         return err
  28.     }
  29.     DB.SingularTable(true)
  30.     return nil
  31. }
  32. //获取Master的信息
  33. func GetMasterInfo()(HostMaster,error){
  34.     var hostMasters []HostMaster
  35.     ret := DB.Find(&hostMasters)
  36.     if ret.Error!=nil{
  37.         return HostMaster{},ret.Error
  38.     }
  39.     if ret.RowsAffected==0 || ret.RowsAffected>1{
  40.         return HostMaster{},errors.New(fmt.Sprintf("HostMaster表中的条目为%d",ret.RowsAffected))
  41.     }
  42.     return hostMasters[0],nil
  43. }
  44. //抢Master与更新心跳
  45. func GrabMaster()error{
  46.     //获取Master的信息
  47.     hostMaster,err := GetMasterInfo()
  48.     if err!=nil{
  49.         return err
  50.     }
  51.     //当前主机为Master则更新心跳.或Master已down则抢Master
  52.     if hostMaster.Master==curHost || time.Now().Sub(*hostMaster.UpdateTime).Seconds()>healthTimeout{
  53.         ret := DB.Model(&HostMaster{}).Where("version = ?",hostMaster.Version).Updates(map[string]interface{}{"master":curHost,"version":hostMaster.Version+1})
  54.         if ret.Error!=nil{
  55.             return errors.New("修改失败: "+ret.Error.Error())
  56.         }
  57.         if ret.RowsAffected==0{
  58.             return nil
  59.         }else{
  60.             if hostMaster.Master==curHost{
  61.                 fmt.Println(curHost+"更新了心跳")
  62.             }else{
  63.                 fmt.Println(curHost+"抢Master成功")
  64.             }
  65.         }
  66.     }
  67.     return nil
  68. }
  69. func main() {
  70.     //初始化数据库
  71.     err := InitDB()
  72.     if err!=nil{
  73.         fmt.Println(err)
  74.         os.Exit(1)
  75.     }
  76.     //周期性更新心跳和抢Master
  77.     go func(){
  78.         for{
  79.             err := GrabMaster()
  80.             if err!=nil{
  81.                 fmt.Println(err)
  82.             }
  83.             time.Sleep(10*time.Second)
  84.         }
  85.     }()
  86.     select {}
  87. }
复制代码



郭少

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

火影

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表