php高并发解决方案有哪些
php高并发解决方案有(不涉及mysql,以及前端):
1、使用文件锁;
2、使用消息队列;
3、如果是分布式集群服务器,需要一个或多个队列服务器;
例子
实际项目中,我们经常遇见抢购商品、抽奖活动等等短时间内访问过高造成高并发的问题。今天使用第一二中方法结合使用
1)文件锁
$file = fopen("list.lock","r");
//LOCK_EX锁定整个文件,LOCK_UN、LOCK_SH锁定文件的一部分,LOCK_NB非阻塞锁
if(!flock($file,LOCK_EX | LOCK_NB)){
return "系统繁忙,请稍后再试";
}
//处理具体业务逻辑
//解除锁定
flock($file,LOCK_UN);
fclose($file );//关闭文件
2)消息队列
/**Redis消息队列**/
/**
* @param $key
* @param $goods_id
* @param $num
* @return void 后台开启抢购后启动这一步
*/
function setNum($key='goods_',$goods_id,$num=100)
{
//实例化(实际开发中提出来)
$redis = new Redis();
//连接服务端
$redis->connect('127.0.0.1', 6379);
$res = $redis->set($key.$goods_id,$num);
}
/**
* 秒杀抢购
* @param Request $request
* @return void
*/
function cusBuy(Request $request)
{
//用户、商品信息
$uid = $request->input('uid');
$goods_id = $request->input('goods_id');
//商品库存key
$key = 'goods_'.$goods_id;
//实例化(实际开发中提出来)
$redis = new Redis();
//连接服务端
$redis->connect('127.0.0.1', 6379);
//监听key 监听对应的key,事务提交之前,如果key被修改,则事务被打断
$redis->watch($key);
//检查key uid 是否存在对应已抢购用户ID
if($redis->hGet('uid',$uid)){
return '请误重复抢购';
}
//获取库存
$goods_num = $this->setNum($key);
//开启事务
$redis->multi();
//判断库存是否足够
if($goods_num>0){
$redis->decr($key);//库存减1
//生成订单
$time = time();
$order_no = 'NO'.rand(1000,9999).$time.date('YmdHis',$time);//生成唯一订单
$data = json_encode(['order_no'=>$order_no,'uid'=>$uid]);
//订单信息入队
$redis->rPush("order_no",$data);
//入队成功后当前抢购人员信息放入redis,避免重复抢购
$redis->set('uid',$uid);
//入队后直接返回抢购成功,异步调用入库订单等等
//关闭事务
$redis->exec();
}
}
3)结合使用
/**
* @param $key
* @param $goods_id
* @param $num
* @return void 后台开启抢购后启动这一步
*/
function setNum($key='goods_',$goods_id,$num=100)
{
//实例化(实际开发中提出来)
$redis = new Redis();
//连接服务端
$redis->connect('127.0.0.1', 6379);
$res = $redis->set($key.$goods_id,$num);
}
/**
* 秒杀抢购
* @param Request $request
* @return void
*/
function cusBuy(Request $request)
{
//$file = fopen("list.lock","r");
//LOCK_EX锁定整个文件,LOCK_UN、LOCK_SH锁定文件的一部分,LOCK_NB非阻塞锁
//if(!flock($file,LOCK_EX | LOCK_NB)){
//return "系统繁忙,请稍后再试";
//}
//处理具体业务逻辑
//用户、商品信息
$uid = $request->input('uid');
$goods_id = $request->input('goods_id');
//商品库存key
$key = 'goods_'.$goods_id;
//实例化(实际开发中提出来)
$redis = new Redis();
//连接服务端
$redis->connect('127.0.0.1', 6379);
//监听key 监听对应的key,事务提交之前,如果key被修改,则事务被打断
$redis->watch($key);
//检查key uid 是否存在对应已抢购用户ID
if($redis->hGet('uid',$uid)){
return '请误重复抢购';
}
//获取库存
$goods_num = $this->setNum($key);
//开启事务
$redis->multi();
//判断库存是否足够
if($goods_num>0){
$redis->decr($key);//库存减1
//生成订单
$time = time();
$order_no = 'NO'.rand(1000,9999).$time.date('YmdHis',$time);//生成唯一订单
$data = json_encode(['order_no'=>$order_no,'uid'=>$uid]);
//订单信息入队
$redis->rPush("order_no",$data);
//入队成功后当前抢购人员信息放入redis,避免重复抢购
$redis->set('uid',$uid);
//入队后直接返回抢购成功,异步调用入库订单等等
//关闭事务
$redis->exec();
}
//解除锁定(文件锁过慢,服务器压力大)
//flock($file,LOCK_UN);
//fclose($file );//关闭文件
}
当然这只是很简单的运用,其中后面订单异步处理出队,订单信息入库,库存更改等等问题。库存字段可以设置为:UNSIGNED。前端涉及到一些异步,页面按钮禁止重复点击等等结合才能更有效处理并发问题