php 使用redis锁限制并发访问类示例


Posted in PHP onNovember 02, 2016

本文介绍了php 使用redis锁限制并发访问类,并详细的介绍了并发访问限制方法。

1.并发访问限制问题

对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功。

例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制的情况下,用户则可以使用同一个换领码同时兑换到多张优惠券。

伪代码如下:

if A(可以换领)
    B(执行换领)
    C(更新为已换领)
D(结束)

如果用户并发提交换领码,都能通过可以换领(A)的判断,因为必须有一个执行换领(B)后,才会更新为已换领(C)。因此如果用户在有一个更新为已换领之前,有多少次请求,这些请求都可以执行成功。

2.并发访问限制方法

使用文件锁可以实现并发访问限制,但对于分布式架构的环境,使用文件锁不能保证多台服务器的并发访问限制。

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

本文将使用其setnx方法实现分布式锁功能。setnx即Set it N**ot eX**ists。

当键值不存在时,插入成功(获取锁成功),如果键值已经存在,则插入失败(获取锁失败)

RedisLock.class.PHP

<?php
/**
 * Redis锁操作类
 * Date:  2016-06-30
 * Author: fdipzone
 * Ver:  1.0
 *
 * Func:
 * public lock  获取锁
 * public unlock 释放锁
 * private connect 连接
 */
class RedisLock { // class start

  private $_config;
  private $_redis;

  /**
   * 初始化
   * @param Array $config redis连接设定
   */
  public function __construct($config=array()){
    $this->_config = $config;
    $this->_redis = $this->connect();
  }

  /**
   * 获取锁
   * @param String $key  锁标识
   * @param Int   $expire 锁过期时间
   * @return Boolean
   */
  public function lock($key, $expire=5){
    $is_lock = $this->_redis->setnx($key, time()+$expire);

    // 不能获取锁
    if(!$is_lock){

      // 判断锁是否过期
      $lock_time = $this->_redis->get($key);

      // 锁已过期,删除锁,重新获取
      if(time()>$lock_time){
        $this->unlock($key);
        $is_lock = $this->_redis->setnx($key, time()+$expire);
      }
    }

    return $is_lock? true : false;
  }

  /**
   * 释放锁
   * @param String $key 锁标识
   * @return Boolean
   */
  public function unlock($key){
    return $this->_redis->del($key);
  }

  /**
   * 创建redis连接
   * @return Link
   */
  private function connect(){
    try{
      $redis = new Redis();
      $redis->connect($this->_config['host'],$this->_config['port'],$this->_config['timeout'],$this->_config['reserved'],$this->_config['retry_interval']);
      if(empty($this->_config['auth'])){
        $redis->auth($this->_config['auth']);
      }
      $redis->select($this->_config['index']);
    }catch(RedisException $e){
      throw new Exception($e->getMessage());
      return false;
    }
    return $redis;
  }

} // class end

?>

demo.php

<?php
require 'RedisLock.class.php';

$config = array(
  'host' => 'localhost',
  'port' => 6379,
  'index' => 0,
  'auth' => '',
  'timeout' => 1,
  'reserved' => NULL,
  'retry_interval' => 100,
);

// 创建redislock对象
$oRedisLock = new RedisLock($config);

// 定义锁标识
$key = 'mylock';

// 获取锁
$is_lock = $oRedisLock->lock($key, 10);

if($is_lock){
  echo 'get lock success<br>';
  echo 'do sth..<br>';
  sleep(5);
  echo 'success<br>';
  $oRedisLock->unlock($key);

// 获取锁失败
}else{
  echo 'request too frequently<br>';
}

?>

测试方法:

打开两个不同的浏览器,同时在A,B中访问demo.php

如果先访问的会获取到锁

输出

get lock success
do sth..
success

另一个获取锁失败则会输出request too frequently

保证同一时间只有一个访问有效,有效限制并发访问。

为了避免系统突然出错导致死锁,所以在获取锁的时候增加一个过期时间,如果已超过过期时间,即使是锁定状态都会释放锁,避免死锁导致的问题。
源码下载地址:点击查看

PHP 相关文章推荐
十天学会php(2)
Oct 09 PHP
php代码收集表单内容并写入文件的代码
Jan 29 PHP
PHP 基于文件头的文件类型验证类函数
May 01 PHP
php对mongodb的扩展(初出茅庐)
Nov 11 PHP
ubuntu10.04配置 nginx+php-fpm模式的详解
Jun 03 PHP
PHP答题类应用接口实例
Feb 09 PHP
Zend Framework自定义Helper类相关注意事项总结
Mar 14 PHP
thinkPHP5.0框架应用请求生命周期分析
Mar 25 PHP
PHP封装的XML简单操作类完整实例
Nov 13 PHP
PHP htmlspecialchars_decode()函数用法讲解
Mar 01 PHP
php实现的数组转xml案例分析
Sep 28 PHP
PHP获取当前时间不准确问题解决方案
Aug 14 PHP
Android AsyncTack 异步任务实例详解
Nov 02 #PHP
php array_pop 删除数组最后一个元素实例
Nov 02 #PHP
PHP设置images目录不充许http访问的方法
Nov 01 #PHP
PHP递归获取目录内所有文件的实现方法
Nov 01 #PHP
php获得文件夹下所有文件的递归算法的简单实例
Nov 01 #PHP
ecshop适应在PHP7的修改方法解决报错的实现
Nov 01 #PHP
遍历echsop的region表形成缓存的程序实例代码
Nov 01 #PHP
You might like
一个php作的文本留言本的例子(六)
2006/10/09 PHP
[原创]php集成安装包wampserver修改密码后phpmyadmin无法登陆的解决方法
2016/11/23 PHP
PHP编程中的Session阻塞问题与解决方法分析
2017/08/07 PHP
基于Web标准的UI组件 — 树状菜单(2)
2006/09/18 Javascript
JS 判断undefined的实现代码
2009/11/26 Javascript
ExtJS4 组件化编程,动态加载,面向对象,Direct
2011/05/12 Javascript
jquery 获取标签名(tagName)示例代码
2013/07/11 Javascript
js验证身份证号有效性并提示对应信息
2015/10/19 Javascript
基于jQuery实现网页打印功能
2015/12/01 Javascript
JavaScript实现相册弹窗功能(zepto.js)
2016/06/21 Javascript
JavaScript实现时间倒计时跳转(推荐)
2016/06/28 Javascript
JS+html5 canvas实现的简单绘制折线图效果示例
2017/03/13 Javascript
EasyUI Datebox 日期验证之开始日期小于结束时间
2017/05/19 Javascript
解决vue-router进行build无法正常显示路由页面的问题
2018/03/06 Javascript
node静态服务器实现静态读取文件或文件夹
2019/12/03 Javascript
javascript前端和后台进行数据交互方法示例
2020/08/07 Javascript
前端 javascript 实现文件下载的示例
2020/11/24 Javascript
Python循环语句中else的用法总结
2016/09/11 Python
使用PyV8在Python爬虫中执行js代码
2017/02/16 Python
Django中的Signal代码详解
2018/02/05 Python
Python实现曲线拟合操作示例【基于numpy,scipy,matplotlib库】
2018/07/12 Python
python使用KNN算法识别手写数字
2019/04/25 Python
莫斯科制造商的廉价皮大衣:Fursk
2020/06/09 全球购物
explicit和implicit的含义
2012/11/15 面试题
本科生学习总结的自我评价
2013/10/02 职场文书
终端业务员岗位职责
2013/11/27 职场文书
酒店销售经理岗位职责
2014/01/31 职场文书
股东协议书范本
2014/04/14 职场文书
经典的毕业生自荐信范文
2014/04/14 职场文书
酒店节能减排方案
2014/05/26 职场文书
青春飞扬演讲稿
2014/09/11 职场文书
个人总结与自我评价
2015/02/14 职场文书
浅谈redis整数集为什么不能降级
2021/07/25 Redis
Nginx部署vue项目和配置代理的问题解析
2021/08/04 Servers
一文弄懂MySQL中redo log与binlog的区别
2022/02/15 MySQL
JDK8中String的intern()方法实例详细解读
2022/09/23 Java/Android