Golang使用etcd构建分布式锁案例
在本教程中,我们将学习如何使用Go和etcd构建分布式锁系统。分布式锁系统对于管理对分布式系统中共享资源的并发访问至关重要。它有助于维护一致性,防止竞争条件,并确保在任何给定时间只有一个进程独占访问资源。
我们将使用Go作为编程语言,并使用etcd作为分布式键值存储。Go的并发特性和对分布式系统的出色支持使其成为本教程的理想选择。Etcd是一种高度可靠的分布式键值存储,被许多大型系统(如Kubernetes)用于配置管理和服务发现。
在本教程结束时,你将能够构建一个分布式锁系统,Go开发人员可以使用该系统来管理对其应用程序中共享资源的访问。
环境准备
要学习本教程,你应该具备:
- 具备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 mainimport ("context""log""time""go.etcd.io/etcd/clientv3"
)type DistributedLock struct {Key stringValue stringLeaseID clientv3.LeaseIDetcdClient *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.IDlog.Printf("Lock acquired: %s", dl.Key)return nil
}
-
ctx context.Context
:这是 Go 语言中用于传递上下文信息的参数,常用于控制操作的超时、取消等情况,比如在分布式环境中协调多个操作的生命周期,确保它们能按照预期执行或者在合适的时候被取消。 -
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函数。该函数将执行以下步骤:
- 删除etcd中的锁键值对
- 解除租赁
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 mainimport ("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.Background()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 sectiontime.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 mainimport ("context""fmt""go.etcd.io/etcd/clientv3""log""time"
)type DistributedLock struct {etcdClient *clientv3.ClientKey stringValue stringLeaseID clientv3.LeaseID
}func (dl *DistributedLock) Lock(ctx context.Context, ttl int64) error {maxRetries := 3 // 最大重试次数,可根据实际情况调整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 retries: %v", maxRetries, err)}// 释放本次申请到的租约,避免资源浪费,虽然租约到期也会自动释放,但及时释放更好_, _ = dl.etcdClient.Revoke(ctx, lease.ID)time.Sleep(retryDelay)continue}dl.LeaseID = lease.IDlog.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构建分布式锁案例
在本教程中,我们将学习如何使用Go和etcd构建分布式锁系统。分布式锁系统对于管理对分布式系统中共享资源的并发访问至关重要。它有助于维护一致性,防止竞争条件,并确保在任何给定时间只有一个进程独占访问资源。 我们将使用Go作为编程语言&am…...
Windows 和 Ubuntu 双系统安装
复现论文的时候,个别包只有Linux版本,并且源码编译比较麻烦,所以干脆直接安装一个双系统(WinUbuntu),方便复现论文。 参考视频链接:Windows 和 Ubuntu 双系统的安装和卸载 0.所需工具 4G以上U…...
多媒体文件解复用(Demuxing)过程
多媒体文件的解复用(Demuxing)过程指的是从一个多媒体容器文件(如 MP4、MKV、AVI 等)中提取不同类型的多媒体数据流(例如视频流、音频流、字幕流等)的过程。 容器文件本身并不包含实际的视频或音频数据&…...
从 Zuul 迁移到 Spring Cloud Gateway:一步步实现服务网关的升级
从 Zuul 迁移到 Spring Cloud Gateway:一步步实现服务网关的升级 迁移前的准备工作迁移步骤详解第一步:查看源码第二步:启动类迁移第三步:引入 Gateway 依赖第四步 编写bootstrap.yaml第五步:替换路由配置第六步&#…...
qt之插件编译
QtXlsxWriter sudo apt install qtbase5-private-dev git clone https://github.com/dbzhang800/QtXlsxWriter.git cd QtXlsxWriter/ qmake make -j6 sudo make install #将生成的lib 及 include copy至项目路径的lib 及include里项目配置: QT xlsxbluetoo…...
pandas一行拆成多行
import pandas as pd df pd.DataFrame({Country:[China,US,Japan,EU,UK/Australia, UK/Netherland],Number:[100, 150, 120, 90, 30, 2],Value: [1, 2, 3, 4, 5, 6],label: list(abcdef)})# 法一 推荐 df2df.drop(Country, axis1).join(df[Country].str.split(/, expandTrue).…...
今天调了个转速的小BUG
同事说转速表有个bug,转速停止后,继电器没有恢复到初始状态。若停止之前是报警,继电器吸合,则停止后继电器还是吸合。我心想不会啊,这软件都弄了好几年了,一直也没出现过状况。 经过与调试同事的沟通&#…...
第三节、电机定速转动【51单片机-TB6600驱动器-步进电机教程】
摘要:本节介绍用定时器定时的方式,精准控制脉冲时间,从而控制步进电机速度 一、计算过程 1.1 电机每一步的角速度等于走这一步所花费的时间,走一步角度等于步距角,走一步的时间等于一个脉冲的时间 w s t e p t … ……...
从一个Bug谈前端响应拦截器的应用
一、问题场景 今天在开发商品管理系统时,遇到了一个有趣的问题:当添加重复的商品编号时,页面同时弹出了两条 "商品编号已存在" 错误提示: 这个问题暴露了前端错误处理机制的混乱,让我们从这个问题出发&…...
JS进阶DAY4|节点操作
嘿👋 今天我们要一起深入探索JavaScript中的DOM操作,这是前端开发中不可或缺的技能。🌟 准备好了吗?让我们一起跳进DOM的海洋,看看怎么用代码操控网页的结构吧! 目录 1. 增加节点 1.1 使用 appendChild 方…...
【Web】2023安洵杯第六届网络安全挑战赛 WP
目录 Whats my name easy_unserialize signal Swagger docs 赛题链接:GitHub - D0g3-Lab/i-SOON_CTF_2023: 2023 第六届安洵杯 题目环境/源码 Whats my name 第一段正则用于匹配以 include 结尾的字符串,并且在 include 之前,可以有任…...
go 语言中协程和GMP模型
为什么需要协程? 协程用来更加精细地利用线程,支撑超高的并发的。协程,从 runtime 的角度看,协程就是一个被调度的 g 结构体。 G 就是协程,M 是线程,P 是为了优化多线程并发时,会抢夺协程队列的…...
coco数据集转换SAM2格式
coco是一个大json汇总了所有train的标签 SAM2训练一张图对应一个json标签 import json import os from pycocotools import mask as mask_utils import numpy as np import cv2def poly2mask(points, width, height):points_array np.array(points, dtypenp.int32).reshape(-…...
【CMD、PowerShell和Bash设置代理】
【CMD、PowerShell和Bash设置代理】 1. CMD(命令提示符)临时设置代理(只对当前会话有效):查看当前代理设置:清除临时代理设置:永久设置代理(对所有新的 CMD 会话有效)&am…...
22智能 代码作业集合
3-2 #include <stdio.h>int main() {int a 21;int b 10;int c ;c a b;printf("Line 1 - c 的值是 %d\n", c );c a - b;printf("Line 2 - c 的值是 %d\n", c );c a * b;printf("Line 3 - c 的值是 %d\n", c );c a / b;printf("…...
实现一个简单的后台架子(侧边栏菜单渲染,折叠,黑白主题,组件主题色,全屏,路由快捷栏)
目录 侧边栏菜单渲染 侧边栏折叠 黑白主题 全屏切换 切换组件主题色 tab快捷栏 代码 侧边栏菜单渲染 结合ElementPlus组件库进行实现 新建的Vue3项目,引入了格式化样式normalize.css和ElementPlus,并进行了全局引入 并进行了全局引入 设置高度为100% 粘贴ElementPlus的…...
vue3-canvas实现在图片上框选标记(放大,缩小,移动,删除)
双图版本(模板对比) 业务描述:模板与图片对比,只操作模板框选的位置进行色差对比,传框选坐标位置给后端,返回对比结果显示 draw.js文件: 新增了 createUuid,和求取两个数组差集的方…...
unity3d—demo(2d人物左右移动发射子弹)
目录 人物代码示例: 子弹代码示例: 总结上面代码: 注意点: 人物代码示例: using System.Collections; using System.Collections.Generic; using UnityEngine;public class PlayerTiao : MonoBehaviour {public f…...
【ETCD】【源码阅读】 深入解析 raftNode.start`函数:Raft 核心启动逻辑剖析
raftNode.start方法 是 etcd 中 Raft 模块的核心启动点,其职责是管理 Raft 状态机的状态变迁、日志处理及集群通信等逻辑。通过对源码的逐行分析,我们将全面揭示其运行机制,探讨其设计背后的分布式系统理念。 函数核心结构 raftNode.start 方…...
Robust Depth Enhancement via Polarization Prompt Fusion Tuning
paper:论文地址 code:github项目地址 今天给大家分享一篇2024CVPR上的文章,文章是用偏振做提示学习,做深度估计的。模型架构图如下 这篇博客不是讲这篇论文的内容,感兴趣的自己去看paper,主要是分享环境&…...
NEFTune,SFT训练阶段给Embedding加噪音
仿照CV里,数据增强的思路(给图像做旋转、反转、改变亮度等);NLP里,SFT训练数据较少时,也可往embedding上加噪音,来增加训练数据的丰富程度。进而提升最终训练效果。 前提假设:Embed…...
uniapp -- 实现页面滚动触底加载数据
效果 首选,是在pages.json配置开启下拉刷新 {"path": "pages/my/document/officialDocument","style": {"navigationStyle":</...
L22.【LeetCode笔记】相交链表(新版)
目录 1.题目 代码模板 2.分析 编辑 算法误区 正确方法1 但不能通过所有的测试用例 修改后 提交结果 正确方法2 节省代码的技巧 1.题目 https://leetcode.cn/problems/3u1WK4/description/ 给定两个单链表的头节点 headA 和 headB ,请找出并返回两个单…...
智能时代网络空间认知安全新观察
文章目录 前言一、历史上的四次认知革命二、人工智能革命掀起认知安全新浪潮三、人工智能技术塑造认知安全新范式四、人工智能治理应对认知安全新思考 前言 12月5日,在2024第三届北外滩网络安全论坛上以“智能时代网络空间认知安全新观察”为主题作主旨演讲&#x…...
游戏如何应对模拟器作弊
模拟器是指能在PC端模拟出安卓手机系统的软件,市面上比较常见的安卓模拟器有:雷电模拟器、MuMu模拟器、夜神模拟器等。 市面上常见的模拟器 模拟器既可以节省手机内存空间,避免长时间玩游戏手机发烫发热的尴尬,也可以用键盘鼠标对…...
c++ 判断一个 IP 地址(可能是 IPv6 或 IPv4)是否属于特定范围
在 C 中,判断一个 IP 地址(可能是 IPv6 或 IPv4)是否属于特定范围时,需要考虑两种不同的地址格式和它们的范围比较。IPv6 和 IPv4 地址结构完全不同,因此需要分别处理这两种地址类型。 实现思路: 识别 IP…...
计算机视觉——相机标定(Camera Calibration)
文章目录 1. 简介2. 原理3. 相机模型3.1 四大坐标系3.2 坐标系间的转换关系3.2.1 世界坐标系到相机坐标系3.2.2 相机坐标系到图像坐标系3.2.3 像素坐标系转换为图像坐标系3.2.4 世界坐标转换为像素坐标 3.3 畸变3.3.1 畸变类型3.3.1.1 径向畸变(Radial Distortion&a…...
【qt环境配置】windows下的qt与vs工具集安装\版本对应关系
vs工具集安装通过vs的在线安装器勾选工具集即可 工具包下载路径:https://www.microsoft.com/zh-cn/download/details.aspx?id40784 配置工具集在qt中可以自动扫描到 《正确在 Windows 上配置 MSVC(2019) 作为 Qt 编译器》https://b3logfile.com/pdf/article/15922…...
GitHub使用
太久不用GitHub发现自己又有些不会了,突发奇想为何不把每次看到的有指导意义的博客收录一下以便下次查阅呢 如何上传文件夹到GitHub上(配图详解)?_github上傳資料夾-CSDN博客 github上如何删除自己的仓库_github删除仓库-CSDN博…...
元宇宙时代的社交平台:Facebook的愿景与实践
随着科技的不断进步,元宇宙(Metaverse)这一概念逐渐走进了人们的视野。作为全球最大的社交平台之一,Facebook(现Meta)在这场元宇宙革命中扮演着重要角色。Meta不仅在不断扩展其社交平台的边界,还…...
精简新闻网站开发/体育新闻最新消息
准备技术: WCF服务契约、数据契约等基础知识 Donet基本开发 内容概要: 三种实例激活类型 单调服务 会话服务 单例服务 实例类型 WCF支持三种实例类型:PerCall、PerSession、Single。PerCall就是单调服务会为每次客户端的请求去分配一个新的服…...
深圳网站优化提供商/seo怎么优化简述
我曾经一直想加入到开源项目中,但是因为没有人指导流程,网上看了很多,基本都是说了个大概,如果你也是一个初出茅庐的人,那么,我将以自己提交的一次开源代码为例,教会你步入开源的世界。1,首先登…...
石家庄业之峰装饰公司怎么样/seo推广效果
转自:http://my.eoe.cn/swer03160828/archive/20067.html 1) 如果在win32 下面编写的cocos2dx 的代码其文件 .cpp,.h 的文件,基本上都其他的平台上面可以直接把文件编辑是没有问题的! 2) 大多数的学习游戏开的人,使用的很多都是 w…...
做灯具外贸哪个网站好做/海外独立站
源码获取:俺的博客首页 "资源" 里下载! 项目介绍 酒店管理系统共分为三个角色,客房经理、前台管理员、客户,各个角色的权限各不相同; 客房经理功能包括:登录、注册、前台用户管理、客房管理、优…...
重庆做营销网站建设/今日重大新闻头条十条
前言:貌似时隔上一篇博客已经过了很久了,这里再写一下关于vi(vim,gvim)的配置 一、vi编辑器的配置文件及目录需要提到的是,vi编辑器的配置文件主要有两个地方1. ~/.vimrc #这里以.开头说明…...
做么网站有黄/足球排名最新排名世界
管理使用者和设立权限的命令命令说明命令说明chmod用来改变权限useradd用来增加用户su用来修改用户5.1 chmod命令chmod命令用来改变许可权限。读取、写入和执行是许可权限中的三个主要设置。因为用户在他们的账号被创建时就被编入一个组群,所以还可以指定那些组群可…...