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函数
Oct 03 PHP
PHP 简单日历实现代码
Oct 28 PHP
深入分析php之面向对象
May 15 PHP
php启用zlib压缩文件的配置方法
Jun 12 PHP
php目录操作实例代码
Feb 21 PHP
PHP贪婪算法解决0-1背包问题实例分析
Mar 23 PHP
PHP Yii框架之表单验证规则大全
Nov 16 PHP
PHP扩展迁移为PHP7扩展兼容性问题记录
Feb 15 PHP
Composer设置忽略版本匹配的方法
Apr 27 PHP
PHP从二维数组得到N层分类树的实现代码
Oct 11 PHP
Zend Framework入门教程之Zend_Mail用法示例
Dec 08 PHP
PHP使用PDO操作sqlite数据库应用案例
Mar 07 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
smarty模板嵌套之include与fetch性能测试
2010/12/05 PHP
PHP 使用pcntl和libevent 实现Timer功能
2013/10/27 PHP
一些有关检查数据的JS代码
2006/09/07 Javascript
jquery下onpropertychange事件的绑定方法
2010/08/01 Javascript
SlideView 图片滑动(扩展/收缩)展示效果
2010/08/01 Javascript
js三种排序算法分享
2012/08/16 Javascript
基于jquery的用鼠标画出可移动的div
2012/09/06 Javascript
jQuery的live()方法对hover事件的处理示例
2014/02/27 Javascript
javascript中setTimeout的问题解决方法
2014/05/08 Javascript
jQuery中on()方法用法实例
2015/01/19 Javascript
JavaScript动态改变表格单元格内容的方法
2015/03/30 Javascript
javascript制作2048游戏
2015/03/30 Javascript
jQuery-1.9.1源码分析系列(十一)DOM操作续之克隆节点
2015/12/01 Javascript
深入解析jQuery中Deferred的deferred.promise()方法
2016/05/03 Javascript
jQuery基本过滤选择器用法示例
2016/09/09 Javascript
bootstrap组件之按钮式下拉菜单小结
2017/01/19 Javascript
第一次记录Bootstrap table学习笔记(1)
2017/05/18 Javascript
angularjs使用gulp-uglify压缩后执行报错的解决方法
2018/03/07 Javascript
vue-cli2.x项目优化之引入本地静态库文件的方法
2018/06/19 Javascript
jQuery常见的遍历DOM操作详解
2018/09/05 jQuery
vue中使用codemirror的实例详解
2018/11/01 Javascript
Vue 中文本内容超出规定行数后展开收起的处理的实现方法
2019/04/28 Javascript
vue实现绑定事件的方法实例代码详解
2019/06/20 Javascript
解决layer.confirm快速点击会重复触发事件的问题
2019/09/23 Javascript
详解JavaScript的this指向和绑定
2020/09/08 Javascript
js仿淘宝放大镜效果
2020/12/28 Javascript
[45:59]完美世界DOTA2联赛PWL S2 FTD vs GXR 第二场 11.22
2020/11/24 DOTA
深入讲解Java编程中类的生命周期
2016/02/05 Python
python进行文件对比的方法
2018/12/24 Python
Python2和Python3中@abstractmethod使用方法
2020/02/04 Python
python使用Word2Vec进行情感分析解析
2020/07/31 Python
军训拉歌口号
2014/06/13 职场文书
2014年会计人员工作总结
2014/12/10 职场文书
初中作文评语
2014/12/25 职场文书
党支部书记岗位职责
2015/02/15 职场文书
P站美图推荐——变身女主角特辑
2022/03/20 日漫