laravel+Redis简单实现队列通过压力测试的高并发处理
秒杀活动
在一般的网络商城中我们会经常接触到一些高并发的业务状况,例如我们常见的秒杀抢购等活动,
在这些业务中我们经常需要处理一些关于请求信息过滤以及商品库存的问题。
在请求中比较常见的状况是同一用户发出多次请求或者包含恶意的攻击,以及一些订单的复购等情况。
而在库存方面则需要考虑超卖这种状况。
下面我们来模拟一个简单可用的并发处理。
直接上代码
代码的流程
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简单实现队列通过压力测试的高并发处理的更多相关文章
- redis的线程模型 与 压力测试
当客户端与ServerSocket产生连接时,会产生一个 AE_REABLE / AE_WRITABL 事件, 多个Socket可能并发产生不同的事件,IO多路复用程序会监听这些Socket,按照顺序 ...
- 简单模拟一下ab压力测试
简单了解下ab ab全程是apache benchmark,是apache官方推出的一个工具,创建多个并发访问线程,模拟多个访问者同时对一个URL地址进行访问.它的测试目标是基于URL的,因此它既可以 ...
- Redis+Restful 构造序列号和压力测试【原创】
[本人原创],欢迎交流和分享技术,转载请附上如下内容:如果你觉得这篇文章对你有帮助,请记得帮我点赞, 谢谢!作者:kevin[转自]http://www.cnblogs.com/itshare/ 很多 ...
- Redis简单延时队列
Redis实现简单延队列, 利用zset有序的数据结构, score设置为延时的时间戳. 实现思路: 1.使用命令 [zrangebyscore keyName socreMin socreMax] ...
- Redis+Restful 构造序列号和压力测试【后续】
大家还记上篇博文https://www.cnblogs.com/itshare/p/8643508.html,测试redis构造流水号的tps是600多/1s. 这个速度显然不能体现redis 集群在 ...
- redis简单消息队列
<?php $redis = new Redis(); $redis->connect('127.0.0.1',6379); $redis->flushall(); $redis-& ...
- 虚拟机压力测试延迟高的可能原因及 ILPIP 配置 / 查询脚本
测试初期 Client VM 的延迟结果正常: 测试后期 Client VM 的延迟偶尔突增/连接失败,越后期超高延迟(比如 30 秒)出现越多: 问题分析 造成这一现象的根本原因很可能是 SNAT( ...
- ab压力测试工具的简单使用
ab是一种用于测试Apache超文本传输协议(HTTP)服务器的工具.apache自带ab工具,可以测试 apache.IIs.tomcat.nginx等服务器 但是ab没有Jmeter.Loadru ...
- 压力测试工具tsung
tsung是用erlang开发的一款简单易用的压力测试工具,可以生成成千上万的用户模拟对服务器进行访问.目前对tsung的理解也仅限于会简单的应用,其内部结构没有深入研究过. 1.安装 tsung是用 ...
随机推荐
- java第11周
1.JPQL查询 JPQL全称Java Persistence Query Language 基于首次在EJB2.0中引入的EJB查询语言(EJB QL),Java持久化查询语言(JPQL)是一种可移 ...
- Tensorflow描述张量的维度:阶,形状以及维数
张量 TensorFlow用张量这种数据结构来表示所有的数据.你可以把一个张量想象成一个n维的数组或列表.一个张量有一个静态类型和动态类型的维数.张量可以在图中的节点之间流通. 阶 在TensorFl ...
- the shortest path algorithm
Dijkstra算法 又称迪杰斯特拉算法,是一个经典的最短路径算法,主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止,使用了广度优先搜索解决赋权有向图的单源最短路径问题,算法最终得到一个最短路 ...
- python基础语法三
集合: 1.不同元素组成 2.无序 3.集合中的元素必须是不可变类型 s = {1, 2, 3 } #定义集合 s = set('hello') print(s) s.pop() #指定删除 s.r ...
- IceGrid 用于Python服务器和客户端通信——参数传递
本篇介绍如何用IceGrid建立python多机通信,传递比较复杂的参数 Ice代码:Demo.ice #ifndef TOS_ICE_COMMON #define TOS_ICE_COMMON mo ...
- 4--TestNG测试报告
第一:自带测试报告(略) 第二:Reportng测试报告(略) 第三:ExtentReport--extentreports.com (1) pom.xml:不是总的pom,是文件夹下的pom < ...
- python3学习笔记及常见问题
1,mac自带的python是2.7版本,我们需要按照python3,这样在terminal下可以直接使用,但是编译打包的时候会默认使用python2.7 解决办法:安装virtualenv,一个管理 ...
- C#中的session用法
Session具有以下特点: (1)Session中的数据保存在服务器端: (2)Session中可以保存任意类型的数据: (2)Session默认的生命周期是20分钟,可以手动设置更长或更短的时间. ...
- oc语言的Foundation框架(学习笔记2)
紧接上文…… 4.集合对象 4.1数组 1.基本概念 Foundation中的数组(NSArray,NSMutableArray)是一组有序的对象集合,通过索引下标获取到数组中的各个元素,也分可变和不 ...
- apply,call以及bind的区别
每个函数都包含两个非继承而来的方法:apply()和 call(). 这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值. 一.apply() apply()方法 ...