1.并发访问限制问题
对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功。
例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制的情况下,用户则可以使用同一个换领码同时兑换到多张优惠券。
伪代码如下:
1 if A(可以换领) 2 B(执行换领) 3 C(更新为已换领) 4 D(结束)
如果用户并发提交换领码,都能通过可以换领(A)的判断,因为必须有一个执行换领(B)后,才会更新为已换领(C)。因此如果用户在有一个更新为已换领之前,有多少次请求,这些请求都可以执行成功。
2.并发访问限制方法
使用文件锁可以实现并发访问限制,但对于分布式架构的环境,使用文件锁不能保证多台服务器的并发访问限制。
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
本文将使用其setnx方法实现分布式锁功能。setnx即Set it Not eXists。
语法:
SETNX key value
将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。
SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。
- 时间复杂度:
- O(1)
- 返回值:
设置成功,返回 1 。
设置失败,返回 0 。
RedisLock.class.php
1 <?php 2 /** 3 * Redis锁操作类 4 * Date: 2016-06-30 5 * Author: fdipzone 6 * Ver: 1.0 7 * 8 * Func: 9 * public lock 获取锁 10 * public unlock 释放锁 11 * private connect 连接 12 */ 13 class RedisLock { // class start 14 15 private $_config; 16 private $_redis; 17 18 /** 19 * 初始化 20 * @param Array $config redis连接设定 21 */ 22 public function __construct($config=array()){ 23 $this->_config = $config; 24 $this->_redis = $this->connect(); 25 } 26 27 /** 28 * 获取锁 29 * @param String $key 锁标识 30 * @param Int $expire 锁过期时间 31 * @return Boolean 32 */ 33 public function lock($key, $expire=5){ 34 $is_lock = $this->_redis->setnx($key, time()+$expire); 35 36 // 不能获取锁 37 if(!$is_lock){ 38 39 // 判断锁是否过期 40 $lock_time = $this->_redis->get($key); 41 42 // 锁已过期,删除锁,重新获取 43 if(time()>$lock_time){ 44 $this->unlock($key); 45 $is_lock = $this->_redis->setnx($key, time()+$expire); 46 } 47 } 48 49 return $is_lock? true : false; 50 } 51 52 /** 53 * 释放锁 54 * @param String $key 锁标识 55 * @return Boolean 56 */ 57 public function unlock($key){ 58 return $this->_redis->del($key); 59 } 60 61 /** 62 * 创建redis连接 63 * @return Link 64 */ 65 private function connect(){ 66 try{ 67 $redis = new Redis(); 68 $redis->connect($this->_config['host'],$this->_config['port'],$this->_config['timeout'],$this->_config['reserved'],$this->_config['retry_interval']); 69 if(empty($this->_config['auth'])){ 70 $redis->auth($this->_config['auth']); 71 } 72 $redis->select($this->_config['index']); 73 }catch(RedisException $e){ 74 throw new Exception($e->getMessage()); 75 return false; 76 } 77 return $redis; 78 } 79 80 } // class end 81 82 ?>
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。





