秒杀活动

在一般的网络商城中我们会经常接触到一些高并发的业务状况,例如我们常见的秒杀抢购等活动,

在这些业务中我们经常需要处理一些关于请求信息过滤以及商品库存的问题。

在请求中比较常见的状况是同一用户发出多次请求或者包含恶意的攻击,以及一些订单的复购等情况。

而在库存方面则需要考虑超卖这种状况。

下面我们来模拟一个简单可用的并发处理。

直接上代码

代码的流程

1.模拟用户请求,将用户写入redis队列中

2.从用户中取出一个请求信息进行处理(可以在这个步骤做更多的处理,请求过滤,订单复购等)

3.用户下单(支付等),减少库存。下面使用了两种方式进行了处理,一种使用了Redis中单线程原子操作的特性使程序一直线性操作从而保持了数据的一致。

另外一种是用了事务进行操作,可以根据业务进行调整,这里就不一一描述了。

实际的业务状况更为复杂,但更多的是出于对基础思路的拓展。

<?php

namespace App\Http\Controllers\SecKill;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis; class SecKillControllers extends Controller { public function SecKillTest() {
     ///在此之前我们已经将一千过用户写入了redis中了
$num = Redis::lpop('user_list');
     ///取出一个用户
     ///
     ///一些对请求的处理
     ///
if (!is_null($num)) {
       ///将需要秒杀的商品放入队列中
$this->AddGoodToRedis(1);
       ///需要注意的是我们如果写的是秒杀活动的话,需要做进一步的处理,例如设置商品队列的缓存等方式,这里就实现了        ///下订单减库存
$this->GetGood(1,$num);
}
} public function DoLog($log) {
file_put_contents("test.txt", $log . '\r\n', FILE_APPEND);
} /**
* 重点在于Redis中存储数据的单线程的原子性,!!!无论多少请求同时执行这个方法,依然是依次执行的!!!!!
* 这种方式性能较高,并且确保了对数据库的单一操作,但容错率极低,一旦出现未可预知的错误会导致数据混乱;
*/
public function GetGood($id,$user_id) {
$good = \App\Goods::find($id);
if (is_null($good)) {
$this->DoLog("商品不存在");
return 'error';
} ///去除一个库存
$num = Redis::lpop('good_list');
///判断取出库存是否成功
if (!$num) {
$this->DoLog("取出库存失败");
return 'error';
} else {
///创建订单
$order = new \App\Order();
$order->good_id = $good->good_id;
$order->user_id = $user_id;
$order->save(); $ok = DB::table('Goods')
->where('good_id', $good->good_id)
->decrement('good_left', $num);
if (!$ok) {
$this->DoLog("库存减少失败");
return;
}
echo '下单成功';
}
} public function AddUserToRedis() {
$user_count = 1000;
for ($i = 0; $i < $user_count; $i++) {
try {
Redis::lpush('user_list', rand(1, 10000));
} catch (Exception $e) {
echo $e->getMessage();
}
}
$user_num = Redis::llen('user_list');
dd($user_num);
} public function AddGoodToRedis($id) { $good = \App\Goods::find($id);
if ($good == null) {
$this->DoLog("商品不存在");
return;
} ///获取当前redis中的库存。
$left = Redis::llen('good_list');
///获取到当前实际存在的库存,库存减去Redis中剩余的数量。
$count = $good->good_left - $left;
// dd($good->good_left);
///将实际库存添加到Redis中
for ($i = 0; $i < $count; $i++) {
Redis::lpush('good_list', 1);
}
echo Redis::llen('good_list');
} public function getGood4Mysql($id) {
DB::beginTransaction();
///开启事务对库存以及下单进行处理
try {
///创建订单
$order = new \App\Order();
$order->good_id = $good->good_id;
$order->user_id = rand(1, 1000);
$order->save(); $good = DB::table("goods")->where(['goods_id' => $id])->sharedLock()->first();
//对商品表进行加锁(悲观锁)
if ($good->good_left) {
$ok = DB::table('Goods')
->where('good_id', $good->good_id)
->decrement('good_left', $num);
if ($ok) {
// 提交事务
DB::commit();
echo'下单成功';
} else {
$this->DoLog("库存减少失败");
}
} else {
$this->DoLog("库存剩余为空");
}
DB::rollBack();
return 'error';
} catch (Exception $e) {
// 出错回滚数据
DB::rollBack();
return 'error';
//执行其他操作
}
} }

AB测试

这里我使用了apache bench对代码进行测试

不了解的可以参阅这篇文章,有非常详细的讲解

https://www.jianshu.com/p/43d04d8baaf7

调用 代码中的

AddUserToRedis()
方法将一堆请求用户放进redis队列中
先看库存

这里设置了一千个库存
开始压力测试

向我们的程序发起1200个请求,并发量为200

这里我们完成了1200个请求,其中标记失败的有1199个。这个是因为apache bench会以第一个请求响应的内容作为基准,

如果后续请求响应内容不一致会标记为失败,如果看到length中标记的数量不要方,基本可以忽略,我们的请求实际是完成了的。

laravel+Redis简单实现队列通过压力测试的高并发处理的更多相关文章

  1. redis的线程模型 与 压力测试

    当客户端与ServerSocket产生连接时,会产生一个 AE_REABLE / AE_WRITABL 事件, 多个Socket可能并发产生不同的事件,IO多路复用程序会监听这些Socket,按照顺序 ...

  2. 简单模拟一下ab压力测试

    简单了解下ab ab全程是apache benchmark,是apache官方推出的一个工具,创建多个并发访问线程,模拟多个访问者同时对一个URL地址进行访问.它的测试目标是基于URL的,因此它既可以 ...

  3. Redis+Restful 构造序列号和压力测试【原创】

    [本人原创],欢迎交流和分享技术,转载请附上如下内容:如果你觉得这篇文章对你有帮助,请记得帮我点赞, 谢谢!作者:kevin[转自]http://www.cnblogs.com/itshare/ 很多 ...

  4. Redis简单延时队列

    Redis实现简单延队列, 利用zset有序的数据结构, score设置为延时的时间戳. 实现思路: 1.使用命令 [zrangebyscore keyName socreMin socreMax] ...

  5. Redis+Restful 构造序列号和压力测试【后续】

    大家还记上篇博文https://www.cnblogs.com/itshare/p/8643508.html,测试redis构造流水号的tps是600多/1s. 这个速度显然不能体现redis 集群在 ...

  6. redis简单消息队列

    <?php $redis = new Redis(); $redis->connect('127.0.0.1',6379); $redis->flushall(); $redis-& ...

  7. 虚拟机压力测试延迟高的可能原因及 ILPIP 配置 / 查询脚本

    测试初期 Client VM 的延迟结果正常: 测试后期 Client VM 的延迟偶尔突增/连接失败,越后期超高延迟(比如 30 秒)出现越多: 问题分析 造成这一现象的根本原因很可能是 SNAT( ...

  8. ab压力测试工具的简单使用

    ab是一种用于测试Apache超文本传输协议(HTTP)服务器的工具.apache自带ab工具,可以测试 apache.IIs.tomcat.nginx等服务器 但是ab没有Jmeter.Loadru ...

  9. 压力测试工具tsung

    tsung是用erlang开发的一款简单易用的压力测试工具,可以生成成千上万的用户模拟对服务器进行访问.目前对tsung的理解也仅限于会简单的应用,其内部结构没有深入研究过. 1.安装 tsung是用 ...

随机推荐

  1. 解决getElementsByClassName()在IE8下的兼容问题

    getElementsByClassName,这个方法让我们可以通过 class 属性中的类名来访问元素,但是IE9 以下的浏览器不支持 .为解决这个问题,我们写一个兼容函数 getByClass() ...

  2. <html> ---- position

    position 固定的属性,是全局的.和DIV层次无关. <!DOCTYPE html> <html lang="en"> <head> &l ...

  3. topological sort

    A topological sortof a dag G  is a linear ordering of all its vertices such that if G contains anedg ...

  4. Linux 驱动——Button驱动4(fasync)异步通知

    button_drv.c驱动文件: #include <linux/module.h>#include <linux/kernel.h>#include <linux/f ...

  5. 2018-软工机试-E-热河路(TLE只拿了90分,待思考)

    单点时限: 2.0 sec 内存限制: 256 MB 没有人在热河路谈恋爱, 总有人在天亮时伤感 如果年轻时你没来过热河路, 那你现在的生活是不是很幸福 ——李志<热河> 奔跑.跌倒.奔跑 ...

  6. Echarts 一个开源图表设计工具

    一般来说,因有所需,方有所求.最近项目中有这方面的需求,用着感觉不错.特此记录!此处仅是一个简单的demo.官网地址:http://echarts.baidu.com/,相关文档.插件都有. 1.js ...

  7. 面试中linux常见的20个命令

    1.查找文件 find / -name filename.txt 根据名称查找/目录下的filename.txt文件. 2.查看一个程序是否运行 ps –ef|grep tomcat 查看所有有关to ...

  8. 前端框架Angular、react、vue在github上的数据统计-2018-05

    2018年5月31日09:15:45 突然想看看几个前端框架的数量,然后就截图了如下数据: 分析: react关注.收藏.Fork都高vue一些, 但相差不大 angular比较奇葩,收藏只有二者一半 ...

  9. flask 手机号码正则匹配的简单操作

    1 导包 Import re 2 匹配邮箱 ret=re.match("[a-zA-Z0-9]{4,20}@163\.com",字符串) If ret: print(ret.gro ...

  10. android 版本更新适配8.0,解决8.0手机无法更新自动安装apk

    随着android 7.0的普及android 8.0的也逐渐流行起来,那么google对权限方面又有了新的修改.而且我发现在android8.0中除了一些bug,比如说:在小米6(Android 8 ...