A. php 使用redis锁限制并发访问类示例
本文介绍了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
保证同一时间只有一个访问有效,有效限制并发访问。
为了避免系统突然出错导致死锁,所以在获取锁的时候增加一个过期时间,如果已超过过期时间,即使是锁定状态都会释放锁,避免死锁导致的问题。
源码下载地址:点击查看
B. PHP 如何在Redis中实现事物(事物提交和事物
public function index()
{
$serv = new \swoole_server("0.0.0.0", 9501);
$serv->set([
'worker_num' => 1,//一般设置为服务器CPU数的1-4倍
'task_worker_num' => 8,//task进程的数量
'daemonize' => 1,//以守护进程执行
'max_request' => 10000,//最大请求数量
"task_ipc_mode " => 2 //使用消息队列通信,并设置为争抢模式
]);
$serv->on('Receive', [$this, 'onReceive']);//接收任务,并投递
$serv->on('Task', [$this, 'onTask']);//可以在这个方法里面处理任务
$serv->on('Finish', [$this, 'onFinish']);//任务完成时候调用
$serv->start();
}
C. thinkphp5模型如何使用redis操作数据库CURD操作
模型中添加如下代码,可实现更新或插入前删除缓存:
protected static function init()
{
TurnGiftSetting::beforeInsert(function ($model) {
$redis = new Redis(config('redis'));
$redis->rm(self::$redisKey);
});
TurnGiftSetting::beforeUpdate(function ($model) {
$redis = new Redis(config('redis'));
$redis->rm(self::$redisKey);
});
TurnGiftSetting::beforeDelete(function ($model) {
$redis = new Redis(config('redis'));
$redis->rm(self::$redisKey);
});
TurnGiftSetting::beforeWrite(function ($model) {
$redis = new Redis(config('redis'));
$redis->rm(self::$redisKey);
});
}
D. 扩展thinkphp5的redis类方法 --- 2021-10-13
thinkphp5的自带redis类方法,只有简单的读取缓存、写入缓存的基本方法,远不能满足我们业务的需求。redis本身支持五种数据类型,string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)。redis的东西就不必再介绍了,接下来还是让我一起扩展thinkphp5的redis方法吧!
说明:这就是实例化对象,包括了options配置项、handler资源句柄、tag标签。实际上这里的handler是new Redis()的对象实例。只要在外部拿到handler资源句柄,则可以对redis的操作进行扩展了。
打印结果如下:
这里需要注意的是:
一个是调thinkphp5的redis的方法,就是这样用。
一个是如果你要调用原生的redis方法
基本的用法都讲完了。
接下来就愉快的开始扩展redis方法吧。
先找到这个类
在里面添加 谈价list 列表的方法;之后可以在里面继续添加其他redis数据类型的操作方法。
E. php redis 需要使用单例吗
<?php
/**
* Class RedisConnManager
*
* 单例模式对redis实例的操作的进一步封装
* 主要目的:防止过多的连接,一个页面只能存在一个声明连接
*
* @author :cuihuan
*/
class RedisManager
{
private static $redisInstance;
/**
* 私有化构造函数
* 原因:防止外界调用构造新的对象
*/
private function __construct(){}
/**
* 获取redis连接的唯一出口
*/
static public function getRedisConn(){
if(!self::$redisInstance instanceof self){
self::$redisInstance = new self;
}
// 获取当前单例
$temp = self::$redisInstance;
// 调用私有化方法
return $temp->connRedis();
}
/**
* 连接ocean 上的redis的私有化方法
* @return Redis
*/
static private function connRedis()
{
try {
$redis_ocean = new Redis();
$redis_ocean->connect(G::$conf['redis-host'], G::$conf['redis-port']);
$redis_ocean->auth(G::$conf['redis-pass']);
}catch (Exception $e){
echo $e->getMessage().'<br/>';
}
return $redis_ocean;
}
}