go版本分布式锁redsync使用教程
redsync使用教程
- 前言
- redsync结构
- Pool结构
- Mutex结构
- acquire加锁操作
- release解锁操作
- redsync包的使用
前言
在编程语言中锁可以理解为一个变量,该变量在同一时刻只能有一个线程拥有,以便保护共享数据在同一时刻只有一个线程去操作。对于高可用的分布式锁应该满足以下条件:
1.互斥:在任意时间内,只有一个客户能够获得一把锁,具有排他性。
2.避免死锁:即使客户端宕机或者从集群中分离了,其它客户端仍然可以获取到该锁
3.容错:只要大部分Redis节点存活,客户端就能正确地获取锁和释放锁。即使锁住某个资源的客户端释放锁之前崩溃或者网络分区仍然能够获取锁和释放锁。
对于Redis高可用集群而言,上述三个条件都非常容易满足,所以适合做分布式锁。
redsync结构
redsync 的通用结构定义如下:
- Pool:抽象连接池
- Conn:抽象每个 Redis 连接
- Script:Redis 脚本
Pool结构
redsync结构的Pools是一个redis.pool数组,每个 redis.Pool 都是上面的 Pool 实现,它代表了一个 Redis 实例的连接池:

Mutex结构
Mutex代表了一个分布式锁,其成员多为 redlock 算法所需要的条件:
// A Mutex is a distributed mutual exclusion lock.
type Mutex struct {name string // 名称expiry time.Duration // 锁的有效时间tries int // 尝试次数delayFunc DelayFunc // 失败尝试设置延迟factor float64 // 误差系数控制quorum int // 投票数 一般为节点数 / 2+1,节点数为奇数genValueFunc func() (string, error) // 加密函数,生成唯一随机串value string // 默认就是唯一随机串until time.Time // 过期时间pools []Pool // 连接池(每个 Pool 指一个 Redis 实例)
}
获取锁的Lock方法实现了redLock的加锁接口,具体实现如下
func (m *Mutex) LockContext(ctx context.Context) error {if ctx == nil {ctx = context.Background()}//生成随机串,base64value, err := m.genValueFunc()if err != nil {return err}//不超过tries次数进行加锁for i := 0; i < m.tries; i++ {if i != 0 {time.Sleep(m.delayFunc(i))}start := time.Now()n, err := func() (int, error) {ctx, cancel := context.WithTimeout(ctx, time.Duration(int64(float64(m.expiry)*m.timeoutFactor)))defer cancel()//尝试异步去获取锁return m.actOnPoolsAsync(func(pool redis.Pool) (bool, error) {return m.acquire(ctx, pool, value)})}()now := time.Now()// 过期时间 = 有效时间值 - 获取锁消耗的时间值 - 有效时间值 * 误差系数until := now.Add(m.expiry - now.Sub(start) - time.Duration(int64(float64(m.expiry)*m.driftFactor)))//成功节点数>=节点数/2+1&& 未过期时,判定加锁成功 if n >= m.quorum && now.Before(until) {m.value = valuem.until = untilreturn nil}func() (int, error) {ctx, cancel := context.WithTimeout(ctx, time.Duration(int64(float64(m.expiry)*m.timeoutFactor)))defer cancel()//获取锁失败,尝试异步去释放锁return m.actOnPoolsAsync(func(pool redis.Pool) (bool, error) {return m.release(ctx, pool, value)})}()if i == m.tries-1 && err != nil {return err}}return ErrFailed
}
time.Sleep(m.delayFunc(i))的失败重试逻辑是当客户端无法获取锁时会设置一个随机值来重试。这个随机值应当和申请锁时间错开,减少脑裂的可能性。此外,还调用了actOnPoolsAsync来实现非阻塞方式同时向多个Redis实例发送set请求。我们来看下actOnPoolsAsync是如何定义的。
func (m *Mutex) actOnPoolsAsync(actFn func(redis.Pool) (bool, error)) (int, error) {type result struct {Node intStatus boolErr error}ch := make(chan result)for node, pool := range m.pools {go func(node int, pool redis.Pool) {r := result{Node: node}r.Status, r.Err = actFn(pool)ch <- r}(node, pool)}n := 0var taken []intvar err errorfor range m.pools {r := <-chif r.Status {n++} else if r.Err != nil {err = multierror.Append(err, &RedisError{Node: r.Node, Err: r.Err})} else {taken = append(taken, r.Node)err = multierror.Append(err, &ErrNodeTaken{Node: r.Node})}}if len(taken) >= m.quorum {return n, &ErrTaken{Nodes: taken}}return n, err
}
acquire加锁操作
func (m *Mutex) acquire(ctx context.Context, pool redis.Pool, value string) (bool, error) {conn, err := pool.Get(ctx)if err != nil {return false, err}defer conn.Close()reply, err := conn.SetNX(m.name, value, m.expiry)if err != nil {return false, err}return reply, nil
}
release解锁操作
func (m *Mutex) release(ctx context.Context, pool redis.Pool, value string) (bool, error) {conn, err := pool.Get(ctx)if err != nil {return false, err}defer conn.Close()//调用Eval,以脚本方式释放锁status, err := conn.Eval(deleteScript, m.name, value)if err != nil {return false, err}return status != int64(0), nil
}
redsync包的使用
该包的使用很简单,具体步骤如下:
- 首先,创建一个Redis的客户端连接;
- 将该客户端连接加入到Redis的Pool中;
- redsync基于该Redis Pool进行实例化;
- 通过redsync实例的NewMutex就可以基于一个具体的key新建一个分布式锁,
该包进行实例化时有基于Redis的单机模式和集群模式两种使用方式,在使用上主要有两种区别: - Redis的客户端是以集群模式还是单机模式创建;
- 在导入redsync包时,集群模式需要导入goredis/v8的版本
具体例子如下:
func main() {//创建redis的客户端连接cli := goredislib.NewClient(&goredislib.Options{Addr: "localhost:6379",})pool := goredis.NewPool(cli)rs := redsync.New(pool)mutexname := "test-global-mutex"mutex := rs.NewMutex(mutexname)if err := mutex.Lock(); err != nil {panic(err)}if ok, err := mutex.Unlock(); !ok || err == nil {panic("unlock failed")}}
相关文章:
go版本分布式锁redsync使用教程
redsync使用教程前言redsync结构Pool结构Mutex结构acquire加锁操作release解锁操作redsync包的使用前言 在编程语言中锁可以理解为一个变量,该变量在同一时刻只能有一个线程拥有,以便保护共享数据在同一时刻只有一个线程去操作。对于高可用的分布式锁应…...
大数据之Hudi数据湖_大数据治理_简介_发展历史_特性_应用场景---大数据之Hudi数据湖工作笔记0001
支持hive spark flink 美国公司开发的~ 都在使用,这些企业都在用 支持hadoop的,更新,插入,删除 和数据增量处理 支持流式数据处理. hive是离线数仓 hive不支持事物 insert overwrite 底层后来通过这种方式支持了事物 insert overwrite处理数据很低效,因为更新是基于覆盖实现…...
射频功率放大器基于纵向导波的杆状构件腐蚀诊断方法的研究
实验名称:基于纵向导波的杆状构件腐蚀诊断方法研究方向:无损探伤测试设备:信号号发生器、安泰ATA-8202功率放大器、数据采集卡、直流电源、超声探头、钢杆、前置放大器。实验过程:图:试验装置试验装置如图3.2所示。监测…...
Leedcode 二分查找 理解1
一个up的理解 一、二分查找基础例题 力扣https://leetcode.cn/problems/binary-search/ 二、二分查找模板问题 带搜索区间分为3个部分: 1、[mid],直接返回 2、[left,mid-1],设置边界right mid - 1 3、[mid1,right]&#x…...
【告别篇】大家好,再见了,我转行了,在筹备创业
前言 相信大家也一直看到我的博客没有更新过了,我其实很久没有打开过博客了,也就意味着我很长一段时间都在停滞不前,没有了学习的动力。 现在我上来是想跟大家告个别 : 很多粉丝宝宝的私信我看了,但是没有回…...
Java——岛屿数量
题目链接 leetcode在线oj题——岛屿数量 题目描述 给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相…...
《代码整洁之道》笔记
1章:专业人士要有专业人士素养,要有责任心,编写代码尽可能完善没有bug,有bug也要勇于承担。坚持学习,坚持练习,保证自己的专业技能。谦虚,相互学习,与顾客达成一致2章:说…...
个人网站如何集成QQ快捷登录功能?
目录 一、网站集成QQ快捷登录的好处 二、网站接入QQ快捷登录具体步骤 (1)登录到QQ互联官网 (2)进行个人开发者认证 (3)创建网站应用 (4)填写网站资料 三、如何在本地开发环境…...
从工厂打螺丝到月薪18k测试工程师,我该满足吗?
以前我比较喜欢小米那句“永远相信美好的事情即将发生”,后来发现如果不努力不可能有美好的事情发生!01高中毕业进厂5年,创业经商多次战败,为了生计辗转奔波高中毕业后我就进了工厂,第一份工作是做模具加工。从500元一…...
【相关分析-高阶绘图】MATLAB实现皮尔逊相关分析-散点直方图
虽然皮尔逊相关分析很常见,但如何更好的展现相关性、散点分布、柱状分布,以提升研究结果的美感和冲击感呢?本文拟通过MATLAB绘制包含散点分布、柱状分布、线性展示的散点直方图,有助于审稿人眼前一亮。 1、Pearson相关系数原理 Pearson相关系数(Pearson Correlation Co…...
Spark性能优化二 Shuffle机制分析
(一) 什么情况下发生shuffle 在MapReduce框架中,Shuffle是连接Map和Reduce之间的桥梁,Map阶段通过shuffle读取数据并输出到对应的Reduce;而Reduce阶段负责从Map端拉取数据并进行计算。在整个shuffle过程中,…...
软测入门(四)Appium-APP移动测试基础
Appium 用来测试手机程序。 测试方面: 功能测试安装卸载测试升级测试兼容测试 Android系统版本不同分辨率不同网络 网络切换、中断测试使用中来电话、短信横竖屏切换 环境搭建 Java安装(查资料)Android SDK安装,配置 HOME和P…...
华为OD机试用Python实现 -【集五福】 |老题且简单
华为OD机试题 最近更新的博客华为 OD 机试 300 题大纲集五福题目描述输入描述输出描述示例一输入输出示例二输入输出代码编写思路Python 代码最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典...
Typro使用以及安装教程来啦
Typora是一款轻便简洁的Markdown编辑器,支持即时渲染技术,这也是与其他Markdown编辑器最显著的区别。即时渲染使得你写Markdown就想是写Word文档一样流畅自如,不像其他编辑器的有编辑栏和显示栏。今天为大家分享下有关Typroa的安装以及使用&a…...
武汉凯迪正大KD305系列智能数字绝缘电阻测试仪
一、概述 KD305系列智能数字绝缘电阻测试仪采用嵌入式工业单片机实时操作系统,数字模拟指针与数字段码显示结合,该系列表具有多种电压输出等级(500V、1000V、2500V、5000V、10000V)、容量大、抗干扰强、模拟指针与数字同步显示、交…...
如何使用码匠连接 Redis
目录 在码匠中集成 Redis 在码匠中使用 Redis 关于码匠 Redis 是由 Salvatore Sanfilippo 用 C 语言开发的一款开源的、高性能的键值对存储数据库,它采用 BSD 协议,为了适应不同场景下的存储需求,提供了多种键值数据类型。到目前为止&…...
防止网络攻击的10大网络安全措施
网络攻击每天都在发生。事实上,每天有超2000次的攻击是针对连接了互联网且未受保护的系统,大概每39s就会发生一次。网络攻击导致的数据泄露、敏感信息被盗、财务损失、声誉受损都给企业及个人带来威胁。随着各大企业对数字系统的依赖,网络威胁已成为当下面临的主要挑战。 实…...
LeetCode 面试题 05.02. 二进制数转字符串
【LetMeFly】面试题 05.02.二进制数转字符串 力扣题目链接:https://leetcode.cn/problems/bianry-number-to-string-lcci/ 二进制数转字符串。给定一个介于0和1之间的实数(如0.72),类型为double,打印它的二进制表达式…...
[MatLab]图像绘制
一、绘制二维图像 1.一张图上绘制一条线 绘制代码如下面所示: x 0:0.01:2*pi; y sin(x); figure %建立幕布 plot(x,y) %绘制图像 %设置图像属性 title(ysin(x)) xlabel(x) ylabel(y)xlim([0 2*pi]) %限制x轴的值域 自定义图线的颜色…...
datax导入到hive的数据量翻倍
现象 mysql->hive 或者oracle->hdfs 源表数据100w 结果hive表数据200w。 这个现象很容易发生,只要你同一时间调度这个json两次。 原因 "writeMode" : "append", "nonconflict","truncate" * appendÿ…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...
