业务场景:秒杀案例
秒杀业务逻辑:多个用户同时抢单,通过mysql行锁抢到的用户进入待支付页面(倒计时)。当用户没有支付订单超时时则取消该订单并归还库存。
应用
thinkphp+redis+workerman(可以自定义命令常驻)
2、安装好redis及扩展。用宝塔的直接搞就完了,过。
3、生产者:用户创建订单向redis插入一条订单数据。
$redis = new \Redis();
$redis->connect('127.0.0.1',6379);
//$redis->auth('密码');//redis有密码就加
/**
*seckill_time为列队名称
*time() + $seckill['pay_time']为到期时间戳
*$newSeckillOrder->id为订单id,可以json字符串存储
*/
$redis->zAdd('seckill_time', time() + $seckill['pay_time'], $newSeckillOrder->id);
4、消费者:这里我们需要一个常驻内存一直来查询这个列队是否有消息,如果有就消费掉。
workerman
/**
* 每个进程启动
* @param $worker
*/
public function onWorkerStart($worker)
{
//防止时间出问题
date_default_timezone_set('PRC');
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
//redis密码
//$redis->auth('密码');//redis有密码就加此句
if($worker->id === 0){
//其他任务
}
//秒杀处理进程
if($worker->id === 1){
echo "启动秒杀任务!\n";
//workerman定时器,每秒执行一次。
Timer::add(1, function() use($redis) {
//通过zRangeByScore查询seckill_time列队中0到当前时间戳的数据。
$res = $redis->zRangeByScore('seckill_time', 0, time());
//存在数据
if (count($res) > 0) {
foreach ($res as $k=>$v){
//处理订单,$res[$k]为生产者存的订单id或数据。
//.....
//消费掉列队中的行数据
$redis->zRem('seckill_time', $res[$k]);
}
//这步不用说都懂的吧!毕竟常驻内存,我们要管理好内存哦~
unset($res);
}
});
}
}
测试结果:
找到appendonly no 改为 appendonly yes
找到appendfsync 设置为 appendfsync everysec