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 相关文章推荐
回答PHPCHINA上的几个问题:URL映射
Feb 14 PHP
10条PHP高级技巧[修正版]
Aug 02 PHP
php笔记之:php函数range() round()和list()的使用说明
Apr 26 PHP
使用array mutisort 实现按某字段对数据排序
Jun 18 PHP
更改localhost为其他名字的方法
Feb 10 PHP
单台服务器的PHP进程之间实现共享内存的方法
Jun 13 PHP
php中mt_rand()随机数函数用法
Nov 24 PHP
php对数组内元素进行随机调换的方法
May 12 PHP
Zend Framework创建自己的动作助手详解
Mar 05 PHP
ThinkPHP框架里隐藏index.php
Apr 12 PHP
php PDO属性设置与操作方法分析
Dec 27 PHP
yii2.0框架数据库操作简单示例【添加,修改,删除,查询,打印等】
Apr 13 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 正则 过滤html 的超链接
2009/06/02 PHP
PHP7 foreach() 函数修改
2021/03/09 PHP
JavaScript arguments 多参传值函数
2010/10/24 Javascript
Knockout自定义绑定创建方法
2015/12/26 Javascript
用JS动态设置CSS样式常见方法小结(推荐)
2016/11/10 Javascript
原生JS获取元素集合的子元素宽度实例
2016/12/14 Javascript
VUE JS 使用组件实现双向绑定的示例代码
2017/01/10 Javascript
JS中Safari浏览器中的Date
2017/07/17 Javascript
纯js实现图片匀速淡入淡出效果
2017/08/22 Javascript
快速理解 JavaScript 中的 LHS 和 RHS 查询的用法
2017/08/24 Javascript
Angular 4根据组件名称动态创建出组件的方法教程
2017/11/01 Javascript
JS实现带阴历的日历功能详解
2019/01/24 Javascript
深入了解JavaScript 的 WebAssembly
2019/06/15 Javascript
Node.js API详解之 net模块实例分析
2020/05/18 Javascript
pip 错误unused-command-line-argument-hard-error-in-future解决办法
2014/06/01 Python
Python中无限元素列表的实现方法
2014/08/18 Python
python下MySQLdb用法实例分析
2015/06/08 Python
Python的地形三维可视化Matplotlib和gdal使用实例
2017/12/09 Python
Anaconda入门使用总结
2018/04/05 Python
python版本的仿windows计划任务工具
2018/04/30 Python
pycharm中使用anaconda部署python环境的方法步骤
2018/12/19 Python
Centos部署django服务nginx+uwsgi的方法
2019/01/02 Python
Python中类的创建和实例化操作示例
2019/02/27 Python
python腾讯语音合成实现过程解析
2019/08/01 Python
html5自动播放mov格式视频的实例代码
2020/01/14 HTML / CSS
goodhealth官方海外旗舰店:新西兰国民营养师
2017/12/15 全球购物
法国发饰品牌:Alexandre De Paris
2018/12/04 全球购物
学生会主席竞聘书
2014/03/31 职场文书
上课迟到检讨书
2015/05/06 职场文书
党务工作者主要事迹材料
2015/11/03 职场文书
大学生干部培训心得体会
2016/01/06 职场文书
Nginx反向代理及负载均衡如何实现(基于linux)
2021/03/31 Servers
sql时间段切分实现每隔x分钟出一份高速门架车流量
2022/02/28 SQL Server
pycharm安装深度学习pytorch的d2l包失败问题解决
2022/03/25 Python
Spring Security动态权限的实现方法详解
2022/06/16 Java/Android
Spring Boot优化后启动速度快到飞起技巧示例
2022/07/23 Java/Android