在redis上实现分布式锁
/**
*在redis上实现分布式锁
*/
class RedisLock {
private $redisString;
private $lockedNames = [];
public function __construct($param = NULL) {
$this->redisString = RedisFactory::get($param)->string;
}
/**
* 加锁
* @param [type] $name 锁的标识名
* @param integer $timeout 循环获取锁的等待超时时间,在此时间内会一直尝试获取锁直到超时,为0表示失败后直接返回不等待
* @param integer $expire 当前锁的最大生存时间(秒),必须大于0,如果超过生存时间锁仍未被释放,则系统会自动强制释放
* @param integer $waitIntervalUs 获取锁失败后挂起再试的时间间隔(微秒)
* @return [type] [description]
*/
public function lock($name, $timeout = , $expire = , $waitIntervalUs = ) {
if ($name == null) return false;
//取得当前时间
$now = time();
//获取锁失败时的等待超时时刻
$timeoutAt = $now + $timeout;
//锁的最大生存时刻
$expireAt = $now + $expire;
$redisKey = "Lock:{$name}";
while (true) {
//将rediskey的最大生存时刻存到redis里,过了这个时刻该锁会被自动释放
$result = $this->redisString->setnx($redisKey, $expireAt);
if ($result != false) {
//设置key的失效时间
$this->redisString->expire($redisKey, $expireAt);
//将锁标志放到lockedNames数组里
$this->lockedNames[$name] = $expireAt;
return true;
}
//以秒为单位,返回给定key的剩余生存时间
$ttl = $this->redisString->ttl($redisKey);
//ttl小于0 表示key上没有设置生存时间(key是不会不存在的,因为前面setnx会自动创建)
//如果出现这种状况,那就是进程的某个实例setnx成功后 crash 导致紧跟着的expire没有被调用
//这时可以直接设置expire并把锁纳为己用
if ($ttl < ) {
$this->redisString->set($redisKey, $expireAt);
$this->lockedNames[$name] = $expireAt;
return true;
}
/*****循环请求锁部分*****/
//如果没设置锁失败的等待时间 或者 已超过最大等待时间了,那就退出
if ($timeout <= || $timeoutAt < microtime(true)) break;
//隔 $waitIntervalUs 后继续 请求
usleep($waitIntervalUs);
}
return false;
}
/**
* 解锁
* @param [type] $name [description]
* @return [type] [description]
*/
public function unlock($name) {
//先判断是否存在此锁
if ($this->isLocking($name)) {
//删除锁
if ($this->redisString->deleteKey("Lock:$name")) {
//清掉lockedNames里的锁标志
unset($this->lockedNames[$name]);
return true;
}
}
return false;
}
/**
* 释放当前所有获得的锁
* @return [type] [description]
*/
public function unlockAll() {
//此标志是用来标志是否释放所有锁成功
$allSuccess = true;
foreach ($this->lockedNames as $name => $expireAt) {
if (false === $this->unlock($name)) {
$allSuccess = false;
}
}
return $allSuccess;
}
/**
* 给当前所增加指定生存时间,必须大于0
* @param [type] $name [description]
* @return [type] [description]
*/
public function expire($name, $expire) {
//先判断是否存在该锁
if ($this->isLocking($name)) {
//所指定的生存时间必须大于0
$expire = max($expire, );
//增加锁生存时间
if ($this->redisString->expire("Lock:$name", $expire)) {
return true;
}
}
return false;
}
/**
* 判断当前是否拥有指定名字的所
* @param [type] $name [description]
* @return boolean [description]
*/
public function isLocking($name) {
//先看lonkedName[$name]是否存在该锁标志名
if (isset($this->lockedNames[$name])) {
//从redis返回该锁的生存时间
return (string)$this->lockedNames[$name] = (string)$this->redisString->get("Lock:$name");
}
return false;
}
}
在redis上实现分布式锁的更多相关文章
- 使用redis构建可靠分布式锁
关于分布式锁的概念,具体实现方式,直接参阅下面两个帖子,这里就不多介绍了. 分布式锁的多种实现方式 分布式锁总结 对于分布式锁的几种实现方式的优劣,这里再列举下 1. 数据库实现方式 优点:易理解 缺 ...
- 一个Redis实现的分布式锁
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.conne ...
- 基于Redis的简单分布式锁的原理
参考资料:https://redis.io/commands/setnx 加锁是为了解决多线程的资源共享问题.Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的 ...
- redis客户端、分布式锁及数据一致性
Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...
- Redis系列(二)--分布式锁、分布式ID简单实现及思路
分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...
- 如何用redis正确实现分布式锁?
先把结论抛出来:redis无法正确实现分布式锁!即使是redis单节点也不行!redis的所谓分布式锁无法用在对锁要求严格的场景下,比如:同一个时间点只能有一个客户端获取锁. 首先来看下单节点下一般r ...
- redis系列:分布式锁
redis系列:分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...
- Redis如何实现分布式锁
今天我们来聊一聊分布式锁的那些事. 相信大家对锁已经不陌生了,我们在多线程环境中,如果需要对同一个资源进行操作,为了避免数据不一致,我们需要在操作共享资源之前进行加锁操作.在计算机科学中,锁(lock ...
- Redis高并发分布式锁详解
为什么需要分布式锁 1.为了解决Java共享内存模型带来的线程安全问题,我们可以通过加锁来保证资源访问的单一,如JVM内置锁synchronized,类级别的锁ReentrantLock. 2.但是随 ...
随机推荐
- LCA最近公共祖先-- HDU 2586
题目链接 Problem Description There are n houses in the village and some bidirectional roads connecting t ...
- 笔记15:Django提升篇
django提升 (1)打包应用程序:可重用性 打包 Python 程序需要工具:setuptools .打包时候建议使用django-appname 1 在你的 Django 项目目录外创建一个名为 ...
- vue-particles粒子动画插件的使用和爬坑出现垂直滚动条
1下载==>cnpm install vue-particles --save-dev 2引入 注册-->main.js//插件 import VueParticles from 'vue ...
- 剖析linux内核中的宏---------container_of
#define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) * __mptr = (ptr); ...
- python27期day12:推导式、内置函数、高阶函数、匿名函数、作业题
1.推导式:做一些有规律的数据结构 列表推导式: 普通循环模式: [加工后的变量 for 循环] 示例一:print([i for i in range(1,51)]) 结果:[1, 2, 3, 4, ...
- plsql导入.dmp步骤
https://blog.csdn.net/yudianxiaoxiao/article/details/78231143 plsql导入.sql和.dmp文件时,会经常用到,对于初学者来说可 ...
- itsdangerous模块
使用场景: 在取消订阅时,可以在URL里序列化并且签名一个用户的ID或在任何的激活账户的链接或类似的情形下使用.这种情况下不需要生成一个一次性的token并把它们存到数据库中. 被签名的对象可以被存入 ...
- nginx日志说明
一.日志说明 nginx日志主要有两种:访问日志和错误日志.访问日志主要记录客户端访问nginx的每一个请求,格式可以自定义:错误日志主要记录客户端访问nginx出错时的日志,格 式不支持自定义.两种 ...
- [LeetCode] 450. Delete Node in a BST 删除二叉搜索树中的节点
Given a root node reference of a BST and a key, delete the node with the given key in the BST. Retur ...
- 树形DP(超详细!!!)
一.概念 1.什么是树型动态规划 树型动态规划就是在“树”的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺 ...