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ÿ…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...