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是用 ...
随机推荐
- tomcat 内存溢出处理方案
找到tomcat7w.exe 在java 页 java options 最后添加 -XX:PermSize=256m-XX:MaxPermSize=512m
- 牛客练习赛 43 B-Tachibana Kanade Loves Probability
链接:https://ac.nowcoder.com/acm/contest/548/B 题目描述 立华奏在学习初中数学的时候遇到了这样一道大水题: “设箱子内有 n 个球,其中给 m 个球打上标记, ...
- 北大poj- 1007
DNA排序 逆序数可以用来描述一个序列混乱程度的量.例如,“DAABEC”的逆序数为5,其中D大于他右边的4个数,E大于他右边的1个数,4+1=5:又如,“ZWQM”的逆序数为3+2+1+0=6. 现 ...
- freeswitch配置功能二
<?xml version="1.0" encoding="utf-8"?><include> <context n ...
- 剑指Offer 60. 把二叉树打印成多行 (二叉树)
题目描述 从上到下按层打印二叉树,同一层结点从左至右输出.每一层输出一行. 题目地址 https://www.nowcoder.com/practice/445c44d982d04483b04a54f ...
- python第一条代码
#!usr/bin/env python #-*-coding:utf-8 -*- print("hello,world")
- svn 部署
安装svn [root@localhost ~]# yum -y install subversion 创建两个目录 一个 数据存储 一个用户密码 [root@localhost ~]# mkdir ...
- NSArray NSMutableArray
NSArray 或者 NSMUtableArray 去除重复的数据: 原来集合操作可以通过valueForKeyPath来实现的,去重可以一行代码实现: [array valueForKeyPath: ...
- hibrnate缓存
缓存: 是计算机领域的概念,它介于应用程序和永久性数据存储源之间. 缓存: 一般人的理解是在内存中的一块空间,可以将二级缓存配置到硬盘.用白话来说,就是一个存储数据的容器.我们关注的是,哪些数据需要被 ...
- 当进行服务端渲染的时间,某些npm包可能会调用document,window这些对象而导致报错
1.在didmount里面使用require引入 require.ensure([], (require) => { this.setState({ picker: require('./Pic ...