php 秒杀活动逻辑

 PHP进阶  2021-06-25  admin  1297  1750

  1. 1.相关数据通过计划任务写入redis,生成静态html前端访问页面

  2. 2.通过redis锁控制秒杀数量

  3. 3.秒杀成功通过队列处理订单

    基于hyperf框架的伪代码:

<?php

declare(strict_types=1);

namespace App\Shop\SecKill\Controller;

class IndexController extends AbstractController
{
    /**
     * redis的keys
     * @var string[]
     */
    private $ary_redis_key = [
        'info' => 'secKill:goods_%s:info',
        'start_time' => 'secKill:goods_%s:start_time',
        'end_time' => 'secKill:goods_%s:end_time',
        'num' => 'secKill:goods_%s:num',
        'success' => 'secKill:goods_%s:success',
    ];

    /**
     * 模拟商品信息
     * @var array
     */
    public $ary_goods = [
        'name' => '测试商品',
        'id' => '101',
        'num' => 1,
        'price' => 100.12,
        //'start_time' => '',
        //'end_time' => '',
    ];

    /**
     * redis连接对象
     * @var Redis
     */
    public $obj_redis;

    public function __construct()
    {
        $this->obj_redis = redis();
    }

    /**
     * 模拟秒杀活动数据
     * 秒杀活动数据提前写入redis缓存中
     * @RequestMapping(path="storeInfo", methods="get,post")
     */
    public function storeInfo()
    {
        $ary_goods = $this->ary_goods;
        //测试,清除redis数据
        foreach ($this->ary_redis_key as $key => $val) {
            $this->obj_redis->del(sprintf($val, $ary_goods['id']));
        }
        //模拟活动开始时间
        $ary_goods['start_time'] = time() + 40;
        $ary_goods['start_date'] = date('Y-m-d H:i:s', $ary_goods['start_time']);
        $ary_goods['end_time'] = $ary_goods['start_time'] + 120;
        $ary_goods['end_date'] = date('Y-m-d H:i:s', $ary_goods['end_time']);


        $redis_info_key = sprintf($this->ary_redis_key['info'], $ary_goods['id']);
        $redis_num_key = sprintf($this->ary_redis_key['num'], $ary_goods['id']);
        $redis_start_time_key = sprintf($this->ary_redis_key['start_time'], $ary_goods['id']);
        $redis_end_time_key = sprintf($this->ary_redis_key['end_time'], $ary_goods['id']);

        $this->obj_redis->set($redis_info_key, json_encode($ary_goods));
        $this->obj_redis->set($redis_num_key, $ary_goods['num']);
        $this->obj_redis->set($redis_start_time_key, $ary_goods['start_time']);
        $this->obj_redis->set($redis_end_time_key, $ary_goods['end_time']);
    }

    /**
     * 模拟秒杀活动进行中
     * @RequestMapping(path="secondKill", methods="get,post")
     */
    public function secondKill(): array
    {
        $ary_goods = $this->ary_goods;
        $obj_redis = $this->obj_redis;

        $redis_start_time_key = sprintf($this->ary_redis_key['start_time'], $ary_goods['id']);
        $redis_end_time_key = sprintf($this->ary_redis_key['end_time'], $ary_goods['id']);
        if (!$obj_redis->exists($redis_end_time_key) || $obj_redis->get($redis_end_time_key) < time()) {
            return [
                'status' => 0,
                'msg' => '活动已结束',
                'date' => date('Y-m-d H:i:s'),
            ];
        }

        if (!$obj_redis->exists($redis_start_time_key) || $obj_redis->get($redis_start_time_key) > time()) {
            return [
                'status' => 0,
                'msg' => '活动未开始',
                'date' => date('Y-m-d H:i:s'),
            ];
        }

        try {
            $redis_key = sprintf($this->ary_redis_key['num'], $ary_goods['id']);
            $redis_success_key = sprintf($this->ary_redis_key['success'], $ary_goods['id']);
            $uid = date('Ymd_His') . '_' . uniqid();

            //获取锁
            $lock_id = RedisLockService::addLock($redis_key, 10);
            if (empty($lock_id)) {
                return [
                    'status' => 0,
                    'msg' => '商品锁定中...',
                    'date' => date('Y-m-d H:i:s'),
                ];
            }

            $num = $obj_redis->get($redis_key);
            if ($num > 0) {
                $obj_redis->zAdd($redis_success_key, ['NX'], time(), $uid);;
                $obj_redis->decrby($redis_key, 1);

                //秒杀成功处理逻辑 begin
                //队列处理逻辑
                //1.生成订单信息
                //2.付款
                for ($i = 0, $sum = 0; $i < 10000; $i++) {
                    $sum += rand(1, 999999);
                }
                //秒杀成功处理逻辑 end

                $res = [
                    'status' => 1,
                    'uid' => $uid,
                    'msg' => '用户下单成功:' . $uid,
                    'date' => date('Y-m-d H:i:s'),
                ];

            } else {
                $res = [
                    'status' => 0,
                    'uid' => $uid,
                    'msg' => '用户下单失败,库存为:' . $num,
                    'date' => date('Y-m-d H:i:s'),
                ];
            }
            //删除锁
            RedisLockService::unLock($redis_key, $lock_id);

            return $res;
        } catch (\Exception $e) {
            return [
                'status' => 0,
                'msg' => $e->getMessage(),
                'date' => date('Y-m-d H:i:s'),
            ];
        }
    }

}

redis锁:

<?php


namespace App\Shop\SecKill\Controller;

/**
 * Class RedisLockService redis 锁服务
 * @author code.cent123.com
 */
class RedisLockService
{
    /**
     * redis锁key
     */
    const REDIS_LOCK_KEY_TEMPLATE = 'LOCKING_KEY_';

    /**
     * redis锁默认超时时间(秒)
     */
    const REDIS_LOCK_DEFAULT_EXPIRE_TIME = 3600;


    /**
     * 加redis锁
     * @param string $strLockKey
     * @param int $intExpireTime 锁过期时间(秒)
     * @return bool|string 加锁成功返回唯一锁ID,加锁失败返回false
     */
    public static function addLock(string $strLockKey, int $intExpireTime = self::REDIS_LOCK_DEFAULT_EXPIRE_TIME)
    {

        if (empty($strLockKey) || $intExpireTime <= 0) {
            return false;
        }

        //获取Redis连接
        $objRedisConn = redis();
        //生成唯一锁ID,解锁需持有此ID
        $uniqueLockId = self::generateUniqueLockId();
        $redisKey = self::REDIS_LOCK_KEY_TEMPLATE . $strLockKey;
        $bolRes = $objRedisConn->set($redisKey, $uniqueLockId, ['nx', 'ex' => $intExpireTime]);

        return $bolRes ? (string)$uniqueLockId : false;
    }

    /**
     * 解redis锁
     * @param string $strLockKey
     * @param string $uniqueLockId
     * @return bool
     */
    public static function unLock(string $strLockKey, string $uniqueLockId): bool
    {

        if (empty($strLockKey) || empty($uniqueLockId)) {
            return false;
        }

        $objRedisConn = redis();
        $redisKey = self::REDIS_LOCK_KEY_TEMPLATE . $strLockKey;;

        //监听Redis key防止在比对lock_id与解锁事务执行过程中被修改或删除,提交事务后会自动取消监控,其他情况需手动解除监控
        $objRedisConn->watch($redisKey);
        if ($uniqueLockId == $objRedisConn->get($redisKey)) {
            $objRedisConn->multi()->del($redisKey)->exec();
            return true;
        }
        $objRedisConn->unwatch();

        return false;
    }

    /**
     * 生成锁唯一ID
     * @return string
     */
    public static function generateUniqueLockId(): string
    {
        return date('Ymd_His') . uniqid('_uniqid_');
        //return redis()->incr('LOCK_UNIQUE_ID');
    }
}


如果文章对您有帮助,点击下方的广告,支持一下作者吧!

转载必须注明出处:

php 秒杀活动逻辑 —— code.cent123.com

相关推荐


Systemd Web 管理系统:简化服务管理

# Systemd Web Systemd Web 是一个 Systemd 的 Web UI 管理系统,通过直观的界面和易用的操作,让用户能够轻松地管理 Linux 系统和服务。该项目旨在简化 Systemd 的使用,使得无论是专业的系统管理员还是普通的用户,都能够方便地对系统进行操作。 ## [github地址] (https://github.com/topascend/systemd

php redis 分布式锁

Redis 分布式锁是一种使用 Redis 数据库来实现的同步机制,用于在分布式系统中保证多个进程或线程对共享资源的互斥访问。Redis 分布式锁的基本原理是使用 Redis 的 SETNX 命令来尝试设置一个键,如果这个键不存在,那么获取锁成功,否则获取锁失败。获取锁成功后,需要设置锁的过期时间,以防止锁被永久占用。释放锁时,需要删除这个键。Redis 分布式锁有一些优点和缺点:优点:Redis

封装 laravel 的公共队列,延迟处理任务

封装 laravel 的公共队列,延迟处理任务&lt;?php /** *ThisfileispartofLunanShopTeam. *http://code.cent123.com/ *@authorhttp://code.cent123.com/view/387 */ namespaceApp\Jobs; useException; useIlluminate\B

php redis加锁解锁

php redis加锁解锁