Golang使用etcd构建分布式锁的示例分享

2025-01-09 16:50

本文主要是介绍Golang使用etcd构建分布式锁的示例分享,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Golang使用etcd构建分布式锁的示例分享》在本教程中,我们将学习如何使用Go和etcd构建分布式锁系统,分布式锁系统对于管理对分布式系统中共享资源的并发访问至关重要,它有助于维护一致性,防止竞...

引言

我们将使用Go作为编程语言,并使用etcd作为分布式键值存储。Go的并发特性和对分布式系统的出色支持使其成为本教程的理想选择。Etcd是一种高度可靠的分布式键值存储,被许多大型系统(如Kubernetes)用于配置管理和服务发现。

在本教程结束时,你将能够构建一个分布式锁系统,Go开发人员可以使用该系统来管理对其应用程序中共享资源的访问。

环境准备

要学javascript习本教程,你应该具备:

  • 具备Go编程语言基础知识
  • 在你的系统上安装Etcd或访问Etcd服务器
  • Go安装在你的系统上(版本1.13或更高版本)

新建Go项目

首先,让我们用必要的依赖项创建一个新的Go项目:

$ mkdir distributed-lock && cd distributed-lock
$ go mod init example.com/distributed-lock
$ go get go.etcd.io/etcd/clientv3

这将设置一个新的Go项目并下载etcd客户端库。

实现加锁和解锁功能

现在是时候实现Lock和Unlock函数了。我们将创建名为lock的新文件。首先导入必要的包并定义结构来保存锁信息:

package main

import (
	"context"
	"log"
	"time"

	"go.etcd.io/etcd/clientv3"
)

type DistributedLock struct {
	Key        string
	Value      string
	LeaseID    clientv3.LeaseID
	etcdClient *clientv3.Client
}

接下来,我们将实现Lock函数。该函数将执行以下步骤:

  • 创建具有指定TTL(生存时间)的新租约
  • 将锁键值对存入附带租约的etcd中
  • 如果锁已被占用,则处理错误并重试
func (dl *DistributedLock) Lock(ctx context.Context, ttl int64) error {
	lease, err := dl.etcdClient.Grant(ctx, ttl)
	if err != nil {
		return err
	}

	_, err = dl.etcdClient.Put(ctx, dl.Key, dl.Value, clientv3.WithLease(lease.ID))
	if err != nil {
		return err
	}

	dl.LeaseID = lease.ID
	log.Printf("Lock acquired: %s", dl.Key)
	return nil
}
  • ctx context.Context:这是 Go 语言中用于传递上下文信息的参数,常用于控制操作的超时、取消等情况,比如在分布式环境中协调android多个操作的生命周期,确保它们能按照预期执行或者在合适的时候被取消。

  • ttl int64:代表 “Time To Live”(存活时间),通常是以秒为单位的时间长度,用于指定分布式锁的有效时长,过了这个时长,锁可能会自动释放,以防止锁被长期占用导致死锁等问题。

  • 这里使用dl.etcdClient再次调用Put方法,它的作用是向etcd中写入键值对(Key-Value)。具体来说,写入的键是dl.Key(从代码推测应该是用于标识这个分布式锁的唯一键,是DistributedLock结构体中的一个字段),值是dl.Value(同样是结构体中的字段,可能是和锁相关的一些其他附属信息等)。

  • 关键的一点是通过clientv3.WithLease(lease.ID)这个选项将前面申请到的租约的ID关联到这个要写入的键值对上,意思是这个键值对的存在时长会受租约的控制,当租约到期(达到ttl设定的时间)时,etcd会自动删除这个键值对,从而实现了分布式锁的自动释放机制。和前面获取租约类似,Put操作也可能出错,所以同样进行错误判断,如果err不为空,就返回错误给调用者。

现在,让我们实现Unlock函数。该函数将执行以下步骤:

  1. 删除etcd中的锁键值对
  2. 解除租赁
func (dl *DistributedLock) Unlock(ctx context.Context) error {
	_, err := dl.etcdClient.Delete(ctx, dl.Key)
	if err != nil {
		return err
	}

	_, err = dl.etcdClient.Revoke(ctx, dl.LeaseID)
	if err != nil {
		return err
	}

	log.Printf("Lock released: %s", dl.Key)
	return nil
}

测试分布式锁

最后,让我们创建一个简单的测试应用程序来查看分布式锁的实际情况。基本上。Go文件,添加以下代码:

package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"go.etcd.io/etcd/clientv3"
)

func main() {
	endpoints := []string{"localhost:2379"}

	cfg := clientv3.Config{
		Endpoints:   endpoints,
		DialTimeout: 5 * time.Second,
	}

	client, err := clientv3.New(cfg)
	if err != nil {
		fmt.Printf("Error connecting to etcd: %v", err)
		os.Exit(1)
	}
	defer client.Close()

	ctx := context.Bahttp://www.chinasem.cnckground()
	lockKey := "my-lock"
	lockValue := "my-value"

	dl := DistributedLock{
		Key:        lockKey,
		Value:      lockValue,
		etcdClient: client,
	}

	err = dl.Lock(ctx, 10)
	if err != nil {
		fmt.Printf("Error acquiring lock: %v", err)
		os.Exit(1)
	}

	// Simulate a critical section
	time.Sleep(5 * time.Second)

	err = dl.Unlock(ctx)
	if err != nil {
		fmt.Printf("Error releasing lock: %v", err)
		os.Exit(1)
	}
}

使用以下命令运行测试应用程序:

$ go run main.go

如果一切正常,您应该看到锁在5秒睡眠后被获取和释放。

重构实现失败重试

以下是在原代码基础上添加申请锁失败重试机制的示例代码,在 Go 语言中可以使用循环结合退避策略等方式来实现,以下是一种常见的实现思路及代码示例,假设使用了time包来进行时间控制以及添加了适当的错误处理和重试次数限制等逻辑:

package main

import (
    "context"
    "fmt"
    "go.etcd.io/etcd/clientv3"
    "log"
    "time"
)

type DistributedLock struct {
    etcdClient *clientv3.Client
    Key        string
    Value      string
    LeaseID    clientv3.LeaseID
}

func (dl *DistributedLock) Lock(ctx context.Context, ttl int64) error {
    maxRetries := 3       //javascript 最大重试次数,可根据实际情况调整
    retryDelay := 1 * time.Second // 初始重试间隔时间,可根据实际情况调整
    for retry := 0; retry < maxRetries; retry++ {
        lease, err := dl.etcdClient.Grant(ctx, ttl)
        if err!= nil {
            if retry == maxRetries-1 {
                return fmt.Errorf("failed to grant lease after %d retries: %v", maxRetries, err)
            }
            // 等待一段时间后重试,这里可以采用退避策略,比如指数退避等,此处简单使用固定间隔
            time.Sleep(retryDelay)
            continue
        }

        _, err = dl.etcdClient.Put(ctx, dl.Key, dl.Value, clientv3.WithLease(lease.ID))
        if err!= nil {
            if retry == maxRetries-1 {
                return fmt.Errorf("failed to put key-value with lease after %d retriesChina编程: %v", maxRetries, err)
            }
            // 释放本次申请到的租约,避免资源浪费,虽然租约到期也会自动释放,但及时释放更好
            _, _ = dl.etcdClient.Revoke(ctx, lease.ID)
            time.Sleep(retryDelay)
            continue
        }

        dl.LeaseID = lease.ID
        log.Printf("Lock acquired: %s", dl.Key)
        return nil
    }
    return fmt.Errorf("exceeded max retries for lock acquisition")
}

出来重试部分,其他逻辑不变,这里解释第二部分重试逻辑:

  • 当租约申请成功后,尝试将键值对写入etcd并关联租约,若这个操作出现错误(err不为nil),同样有如下处理:
  • 先判断是否达到最大重试次数,如果是,返回带有详细失败信息的error告知调用者设置键值关联租约操作经过多次重试后失败以及具体错误原因。
  • 如果没达到最大重试次数,为了避免已经申请到的租约一直占用资源(虽然租约到期会自动释放,但及时主动释放更合理),调用dl.etcdClient.Revoke方法来撤销(释放)刚刚申请到的租约,然后让当前协程暂停执行retryDelay设定的时间间隔后,通过continue进入下一次循环,再次尝试整个申请锁的流程。

总结

在本教程中,我们使用Go等语言构建了简单分布式锁系统。该系统可用于远程Go开发人员管理对其应用程序中共享资源的访问,确保分布式系统中的一致性和防止竞争条件。

以上就是golang使用etcd构建分布式锁的示例分享的详细内容,更多关于Golang etcd分布式锁的资料请关注China编程(www.chinasem.cn)其它相关文章!

这篇关于Golang使用etcd构建分布式锁的示例分享的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1152997

相关文章

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Python虚拟环境与Conda使用指南分享

《Python虚拟环境与Conda使用指南分享》:本文主要介绍Python虚拟环境与Conda使用指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、python 虚拟环境概述1.1 什么是虚拟环境1.2 为什么需要虚拟环境二、Python 内置的虚拟环境工具

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

LiteFlow轻量级工作流引擎使用示例详解

《LiteFlow轻量级工作流引擎使用示例详解》:本文主要介绍LiteFlow是一个灵活、简洁且轻量的工作流引擎,适合用于中小型项目和微服务架构中的流程编排,本文给大家介绍LiteFlow轻量级工... 目录1. LiteFlow 主要特点2. 工作流定义方式3. LiteFlow 流程示例4. LiteF