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是用 ...
随机推荐
- deepfake-faceswap第一篇论文-2016摘要
核心目标:给定一个人的单张图片A,另一个人的单张图片B,在保持姿势,面部表情,视线方向,发型和光照不变的条件下,将A图片中的人物换成B图片中的人物.2016年,文章[1]实现了这个目标: 德国的蒂宾根 ...
- OpenGL创建一个三角形,并且颜色渐变(绿—>黑—>绿)
学习自: https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/#_4 #include <glad/glad ...
- vue中使用ajax
var vue = new Vue({ el:"#vueid", data:{ selectById : "", }, methods:{ yourMethod ...
- 如何使用HTML5的WebSocket实现网页与服务器的双工通信(一)
本系列服务端双工通信包括两种实现方式:一.使用Socket构建:二.使用WCF构建.本文为使用Socket构建服务端的双工通信,客户端同样使用Html5的WebSocket技术进行调用. 一.网页客户 ...
- 「LuoguP1280」尼克的任务
LuoguP1280尼克的任务 : 线性dp 题目描述 尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构 ...
- 二进制32位转十进制int
public class BinaryToDecimal { public static int BinaryToDecimal(int binaryNumber){ int decimal = 0; ...
- JSF的分析
一.JSF的有关定义 JavaServer Faces (JSF) 是一种用于构建Java Web 应用程序的标准框架 它提供了一种以组件为中心来开发 Java Web 用户界面的方法,从而简化了开发 ...
- 利用axure软件实现app中的轮播图功能
1.首先在axure软件中插入一张手机模型图片并调整为合适大小 2.在需要展示轮播图片位置拖入[动态面板]并且调整大小 拖入后双击动态面板,填入面板名称,并且添加面板状态(此处轮播图为三张,所以有三个 ...
- 增长java中数组的长度
package month_201711; import java.util.Arrays;/** * 数组长度+1 * @author watchfree * */public class Main ...
- Deepest left leaf node in a binary tree
Recursion selfcontained recursion global variables outside of recursion Recursion Design Whenever r ...