标签:数据 this localhost timeout ram 不同 测试 网络 exception
1.并发访问限制问题
对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功。
例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制的情况下,用户则可以使用同一个换领码同时兑换到多张优惠券。
伪代码如下:
if A(可以换领)
B(执行换领)
C(更新为已换领)
D(结束)
如果用户并发提交换领码,都能通过可以换领(A)的判断,因为必须有一个执行换领(B)后,才会更新为已换领(C)。因此如果用户在有一个更新为已换领之前,有多少次请求,这些请求都可以执行成功。
2.并发访问限制方法
使用文件锁可以实现并发访问限制,但对于分布式架构的环境,使用文件锁不能保证多台服务器的并发访问限制。
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
本文将使用其setnx方法实现分布式锁功能。setnx即Set it N**ot eX**ists。
当键值不存在时,插入成功(获取锁成功),如果键值已经存在,则插入失败(获取锁失败)
2.简单案例
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 ?>
demo.php
1 <?php 2 require ‘RedisLock.class.php‘; 3 4 $config = array( 5 ‘host‘ => ‘localhost‘, 6 ‘port‘ => 6379, 7 ‘index‘ => 0, 8 ‘auth‘ => ‘‘, 9 ‘timeout‘ => 1, 10 ‘reserved‘ => NULL, 11 ‘retry_interval‘ => 100, 12 ); 13 14 // 创建redislock对象 15 $oRedisLock = new RedisLock($config); 16 17 // 定义锁标识 18 $key = ‘mylock‘; 19 20 // 获取锁 21 $is_lock = $oRedisLock->lock($key, 10); 22 23 if($is_lock){ 24 echo ‘get lock success<br>‘; 25 echo ‘do sth..<br>‘; 26 sleep(5); 27 echo ‘success<br>‘; 28 $oRedisLock->unlock($key); 29 30 // 获取锁失败 31 }else{ 32 echo ‘request too frequently<br>‘; 33 } 34 35 ?>
测试方法:
打开两个不同的浏览器,同时在A,B中访问demo.php
如果先访问的会获取到锁
输出
get lock success
do sth..
success
另一个获取锁失败则会输出request too frequently
保证同一时间只有一个访问有效,有效限制并发访问。
为了避免系统突然出错导致死锁,所以在获取锁的时候增加一个过期时间,如果已超过过期时间,即使是锁定状态都会释放锁,避免死锁导致的问题
标签:数据 this localhost timeout ram 不同 测试 网络 exception
原文地址:http://www.cnblogs.com/lq527/p/6228477.html