当前位置: 首页 > news >正文

golang 基于redis实现集群中的主实例选举

在集群中,有些业务逻辑只需要1个实例去执行,例如定时通知、任务调度器等。本文通过redis实现了在集群中选举一个master实例。

package redisutilimport ("context""errors""fmt""time""github.com/google/uuid""github.com/go-redis/redis/v9"
)const expiration = 20 * time.Secondvar instanceID = func() string {ips := util.HostIPsif len(ips) == 0 {ips = []string{"unknown_ip"}}return fmt.Sprintf("%s:%s", ips[0], uuid.New().String())
}()// AcquireAsMaster 选举master。如果当前节点选中为master,则向后执行;如果没选中,则阻塞等待下一次选举
func AcquireAsMaster(ctx context.Context, client redis.Client, lockName string) (instance string, release func() error, err error) {for wait := 5; ; wait = (wait << 1) % 75 { // 5, 10, 20, 40, 5, 10, ...select {case <-ctx.Done():return instanceID, func() error { return nil }, ctx.Err()default:}masterKey := fmt.Sprintf("%s:%s:master", strgen.ClusterTopic(), lockName)beElected, err := client.SetNX(ctx, masterKey, instanceID, expiration).Result()if err != nil || !beElected {time.Sleep(time.Duration(wait) * time.Second) // 选举出错了 or 选举落选,指数退避重新选举}if beElected {// 启动看门狗无限续期,防止master身份丢失go watchDog(ctx, client, masterKey)// 返回释放函数releaseFunc := func() error {released, e := client.Do(context.Background(), "EVAL", `if redis.call("GET", KEYS[1]) == ARGV[1] thenreturn redis.call("DEL", KEYS[1])endreturn 0`, 1, masterKey, instanceID).Result()if e != nil {return fmt.Errorf("failed to release as master: %w", e)}if released == 0 {return errors.New("failed to release as master: not master")}return nil}return instanceID, releaseFunc, nil}}
}// 看门狗无限需求,直到ctx结束
func watchDog(ctx context.Context, c redis.Client, masterK string) {ticker := time.NewTicker(expiration / 3)defer ticker.Stop()for {select {case <-ctx.Done():returncase <-ticker.C:success, er := c.Do(ctx, "EVAL", `if redis.call("GET", KEYS[1]) == ARGV[1] thenreturn redis.call("EXPIRE", KEYS[1], ARGV[2])endreturn 0`, 1, masterK, instanceID, int(expiration.Seconds())).Result()if er != nil || success == 0 {return}}}
}

使用示例:

instanceID, release, err := redisutil.AcquireAsMaster(ctx, myClient, "test-worker")
defer release()
if err != nil {xlog.Error(ctx, "failed to be master: %+v", err)return
}
xlog.Info(ctx, "%s becomes master.", instanceID)

相关文章:

  • leetcode动态规划—打家劫舍系列
  • 信创国产化
  • vue3实现鼠标悬浮div动画效果
  • espefuse.py烧录MAC地址
  • 若依项目天气模块
  • PHP中文网文章内容提取免费API接口教程
  • Cypress API 中文详解
  • Python基于Django的校园打印预约系统(附源码,文档说明)
  • LangChain实战:MMR和相似性搜索技术应用
  • 01 redis 的环境搭建
  • 第六章 进阶14 项目周报的妙用
  • 湖北理元理律师事务所:债务优化服务的流程透明度建设
  • 基于Matlab实现卫星轨道模拟仿真
  • 抗辐照加固CANFD芯片:以车规级设计提升商业航天系统可靠性
  • MySQL高可用集群
  • 小黑大语言模型应用探索:langchain智能体构造源码demo搭建1(初步流程)
  • QEMU/KVM课程大纲暨学习路线(1)
  • 通义灵码2.5——基于编程智能体开发Wiki多功能搜索引擎
  • 多卡训练核心技术详解
  • 【Go语言】Fyne GUI 库使用指南 (面向有经验开发者)
  • 网站开发公司怎么查询/电商数据查询平台
  • 优质网站建设/爱站seo查询软件
  • 网站建设的概念/2023年8月新闻热点事件
  • 做视频点播网站要多少带宽/金华百度seo
  • 网站icp备案时间/it培训四个月骗局
  • .net做的大型网站吗/详情页页面页面