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 杂谈《重构-改善既有代码的设计》之四 简化条件表达式
Apr 09 PHP
深入php数据采集的详解
Jun 02 PHP
解析PHP处理换行符的问题 \r\n
Jun 13 PHP
PHP+Ajax检测用户名或邮件注册时是否已经存在实例教程
Aug 23 PHP
php读取mssql的ntext字段返回值为空的解决方法
Dec 30 PHP
避免Smarty与CSS语法冲突的方法
Mar 02 PHP
php筛选不存在的图片资源
Apr 28 PHP
在Mac OS上编译安装Nginx+PHP+MariaDB开发环境的教程
Feb 23 PHP
YII Framework框架教程之日志用法详解
Mar 14 PHP
PHP数组常用函数实例小结
Aug 20 PHP
PHP pthreads v3使用中的一些坑和注意点分析
Feb 21 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
MYSQL数据库初学者使用指南
2006/11/16 PHP
php 学习资料零碎东西
2010/12/04 PHP
PHP 日,周,月点击排行统计
2012/01/11 PHP
Php图像处理类代码分享
2012/01/19 PHP
coreseek 搜索英文的问题详解
2013/06/08 PHP
thinkphp跨库操作的简单代码实例
2016/09/22 PHP
(仅IE下有效)关于checkbox 三态
2007/05/12 Javascript
JQuery 图片延迟加载并等比缩放插件
2009/11/09 Javascript
javascript学习笔记(十二) RegExp类型介绍
2012/06/20 Javascript
jQuery使用ajaxSubmit()提交表单示例
2014/04/04 Javascript
js子页面获取父页面数据示例
2014/05/15 Javascript
JavaScript中的console.time()函数详细介绍
2014/12/29 Javascript
详解JavaScript中循环控制语句的用法
2015/06/03 Javascript
Bootstrap 源代码分析(未完待续)
2016/08/17 Javascript
JavaScript实现图片轮播组件代码示例
2016/11/22 Javascript
js实现音频控制进度条功能
2017/04/01 Javascript
浅谈jQuery框架Ajax常用选项
2017/07/08 jQuery
使用JavaScript实现表格编辑器(实例讲解)
2017/08/02 Javascript
vue中的$emit 与$on父子组件与兄弟组件的之间通信方式
2018/05/13 Javascript
Vue路由管理器Vue-router的使用方法详解
2020/02/05 Javascript
jquery实现简易验证插件封装
2020/09/13 jQuery
Python生成随机数的方法
2014/01/14 Python
浅谈python中的面向对象和类的基本语法
2016/06/13 Python
Python 查找字符在字符串中的位置实例
2018/05/02 Python
PyCharm+PySpark远程调试的环境配置的方法
2018/11/29 Python
python+openCV利用摄像头实现人员活动检测
2019/06/22 Python
Django 对象关系映射(ORM)源码详解
2019/08/06 Python
Python Gitlab Api 使用方法
2019/08/28 Python
pytorch实现focal loss的两种方式小结
2020/01/02 Python
西班牙购买行李箱和背包网站:Maletas Greenwich
2019/10/08 全球购物
Hibernate持久层技术
2013/12/16 面试题
环境工程专业个人求职信
2013/12/05 职场文书
创业女性典型材料
2014/05/02 职场文书
车辆年检委托书范本
2014/10/14 职场文书
个人房屋租赁合同(标准范本)
2019/09/16 职场文书
Python+OpenCV实现在图像上绘制矩形
2022/03/21 Python