首页 > 数据库 >Golang: Redislock源码分析

Golang: Redislock源码分析

时间:2024-03-23 15:55:54浏览次数:24  
标签:end -- redis Golang lua 源码 Redislock key ttl

Golang: Redislock源码分析

源码

https://github.com/bsm/redislock

实现

Lua脚本

obtain.lua

-- obtain.lua: arguments => [value, tokenLen, ttl]
-- Obtain.lua try to set provided keys's with value and ttl if they do not exists.
-- Keys can be overriden if they already exists and the correct value+tokenLen is provided. 

-- 设置过期时间
local function pexpire(ttl) 
	-- Update keys ttls.
	for _, key in ipairs(KEYS) do
		redis.call("pexpire", key, ttl)
	end
end

-- canOverrideLock check either or not the provided token match
-- previously set lock's tokens.
-- 判断某个Key是否已经被占用了, 如果是自己占用的, 那么则可以尝试覆盖
local function canOverrideKeys() 
	local offset = tonumber(ARGV[2])

	for _, key in ipairs(KEYS) do
		if redis.call("getrange", key, 0, offset-1) ~= string.sub(ARGV[1], 1, offset) then
			return false
		end
	end
	return true
end

-- Prepare mset arguments.
local setArgs = {}
for _, key in ipairs(KEYS) do
	table.insert(setArgs, key)
	table.insert(setArgs, ARGV[1])
end

-- 尝试范围性的使用setnx , 如果有值已经存在, 则尝试覆盖, 如果没法覆盖, 则失败.
if redis.call("msetnx", unpack(setArgs)) ~= 1 then
	if canOverrideKeys() == false then
		return false
	end
	redis.call("mset", unpack(setArgs))
end

pexpire(ARGV[3])
return redis.status_reply("OK")

refresh.lua

-- refresh.lua: => Arguments: [value, ttl]
-- refresh.lua refreshes provided keys's ttls if all their values match the input. 

-- Check all keys values matches provided input.
-- 检查是否拥有以上锁的权限
local values = redis.call("mget", unpack(KEYS))
for i, _ in ipairs(KEYS) do
	if values[i] ~= ARGV[1] then
		return false
	end
end

-- 如果有, 则刷新keys的ttl
for _, key in ipairs(KEYS) do
	redis.call("pexpire", key, ARGV[2]) 
end

return redis.status_reply("OK")

release.lua

-- release.lua: => Arguments: [value]
-- Release.lua deletes provided keys if all their values match the input. 

-- Check all keys values matches provided input.
-- 检查是否拥有以上锁的权限, 如果没有所有的权限, 则取消
local values = redis.call("mget", unpack(KEYS))
for i, _ in ipairs(KEYS) do
	if values[i] ~= ARGV[1] then
		return false
	end
end
-- 删除所有的可以
-- Delete keys.
redis.call("del", unpack(KEYS))

return redis.status_reply("OK")

pttl.lua

-- pttl.lua: => Arguments: [value]
-- pttl.lua returns provided keys's ttls if all their values match the input. 

-- Check all keys values matches provided input.
-- 检查是否拥有以上锁的权限, 如果没有所有的权限, 则取消
local values = redis.call("mget", unpack(KEYS))
for i, _ in ipairs(KEYS) do
	if values[i] ~= ARGV[1] then
		return false
	end
end

-- 返回所有key的最短ttl
local minTTL = 0
for _, key in ipairs(KEYS) do
	local ttl = redis.call("pttl", key)
	-- Note: ttl < 0 probably means the key no longer exists.
	if ttl > 0 and (minTTL == 0 or ttl < minTTL) then
		minTTL = ttl
	end
end
return minTTL

Golang实现

引入lua脚本

在Reids中, 可以直接运行lua脚本

在实现中, 脚本的引入通过 //go:embed {filename}​可以非常方便的实现

基本结构

​​Client​

type Client struct {
	client RedisClient
	tmp    []byte
	tmpMu  sync.Mutex
}

这里的设计很蠢, tmp是用来获取一个随机的token的, 按理说这个token作为校验应该和lock绑定, 但是绑定在了client上, 这部分没有任何阅读的必要.

Lock

type Lock struct {
	*Client
	keys     []string
	value    string
	tokenLen int
}

需要稍微注意下的是tokenLen​, 因为实际的Key对应的Value是Token + Metadata, 所以在校验的时候不能直接获取value判断而是前缀判断.

metadata​可以设置一些和服务实例相关的信息, 这部分的设计还有有考虑的.

获取锁

  1. 生成一个Token, 作为标识符, 添加metadata信息, 辅助后期debug

  2. 设置ttl, 重试策略

    • 最大重试次数

    • 指数退避算法

    • 间隔重试, 直至成功

  3. 检查是否已经设置了最大超时时间, 如果没有设置, 默认使用ttl作为超时时间

  4. 不断尝试获取锁, 如果没有获取, 根据重试策略直接进行重试, 或超时返回

获取TTL

使用pttl.lua进行最小ttl的获取

刷新TTL

使用refresh.lua更新所有的key的ttl

释放

调用release.lua删除所有占用的key

注意

Redis: msetnx

总结

lua代码的设计还稍微值得学习看看

Go代码没有什么特别值得学习的地方, 如果能够提供一个watch-dog​的方式, 可能会更好一些, 现在的使用没有特别方便.

标签:end,--,redis,Golang,lua,源码,Redislock,key,ttl
From: https://www.cnblogs.com/pDJJq/p/18091212/golang-redislock-source-code-analysis-26vwts

相关文章

  • 【附源码】JAVA计算机毕业设计音乐平台设计与实现(springboot+mysql+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展,互联网已经渗透到人们生活的方方面面,音乐作为人们日常生活的重要娱乐方式,其在线化、平台化的发展趋势日益明显。近年来,音乐平......
  • 【附源码】JAVA计算机毕业设计音乐平台的设计(springboot+mysql+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着数字技术的迅猛发展,音乐产业正在经历一场深刻的变革。传统音乐销售模式逐渐式微,而在线音乐平台以其便捷性、多样性和互动性,迅速占领了市场。当前......
  • 游戏开发:移植golang共享库 for lua
    一些奇奇怪怪的尝试:)随笔记录下将golang模块导出为共享库供lua使用(一般用于功能模块适配和迁移),这里给出一个借助c语言实现中间层通信的方案(不要问我为什么不使用ffi)。假设使用go实现底层模块core,export相关API(如下例的G_Signature)供外部使用,这里是被C层使用。那么需要将go模块编......
  • C#获取HTML源码
     C#获取HTML源码2024年03月23日记录以前的那个从网上找到的方法,在一些网站上用不了,如17K,取出来的是乱码,要么就是一坨JS,好像是用JS又重新加载了什么的 usingSystem;usingSystem.Collections.Generic;usingSystem.Web;usingSystem.Net;usingSystem.IO;using......
  • 基于SpringBoot+Vue的宠物猫售卖管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我自己的网站自己的小程序(小蔡coding)代码参考数据库参考源码获取前言......
  • 【Golang星辰图】实现弹性微服务架构:使用go-micro和go-kit构建可扩展的网络应用
    构建高效网络应用:探索分布式系统和微服务的利器前言在当今的互联网时代,构建可扩展且可靠的网络应用变得越来越重要。分布式系统和微服务架构成为了解决大规模应用程序开发和管理的有效方法。本文将介绍一些用于构建分布式系统和微服务的关键工具和库,例如go-rpc、go-micro......
  • 手机直播源码,前端图片压缩上传需顾及清晰度问题
    手机直播源码,前端图片压缩上传需顾及清晰度问题这里我采用element的文件上传控件来上传图片:<el-uploadclass="avatar-uploader":action="GLOBAL.serverFileUrl"name="file"drag:show-file-list="false":on-change="beforeAvatarUpload"......
  • [附源码]JAVA计算机毕业设计车辆维修管理系统(源码+开题)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着汽车行业的迅猛发展,车辆数量不断增加,车辆维修管理已成为一个亟待解决的问题。传统的车辆维修管理方式往往依赖于纸质记录和人工操作,效率低下且容......
  • [附源码]JAVA计算机毕业设计车辆理赔系统(源码+开题)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着汽车产业的快速发展,车辆保险业务逐渐成为保险公司的重要收入来源。然而,传统的车辆理赔流程繁琐,效率低下,难以满足现代快节奏生活的需求。因此,开发......
  • 基于springboot的线上买菜系统(含源码+sql+视频导入教程)
    ......