ThinkPHP 6.0高并发秒杀系统开发全攻略 | 电商系统实战教程

2025-08-12 0 606

一、秒杀系统架构设计

本教程将使用ThinkPHP 6.0构建一个能够支撑高并发的秒杀系统,解决瞬时高流量带来的系统压力问题。

核心技术栈:

  • 核心框架:ThinkPHP 6.0
  • 缓存系统:Redis 5.0+
  • 消息队列:Redis List/Stream
  • 前端技术:Vue.js + Element UI
  • 压力测试:JMeter

系统架构图:

用户请求 → 负载均衡 → Web服务器 → [限流] → [缓存验证] → [队列处理] → 数据库
                                   │          │           │
                                   ↓          ↓           ↓
                                Nginx     Redis缓存    Redis队列
        

核心解决方案:

  1. 页面静态化减少服务器压力
  2. Redis原子操作控制库存
  3. 消息队列异步处理订单
  4. 分布式锁防止超卖
  5. 接口限流保护系统

二、项目初始化与配置

1. 创建ThinkPHP项目

composer create-project topthink/think tp6-seckill
cd tp6-seckill

2. 安装扩展依赖

composer require predis/predis
composer require topthink/think-multi-app
composer require topthink/think-queue

3. 项目目录结构

tp6-seckill/
├── app/
│   ├── controller/      # 控制器
│   ├── model/           # 模型
│   ├── service/         # 服务层
│   ├── job/            # 队列任务
│   ├── middleware/      # 中间件
│   └── view/           # 视图文件
├── config/
│   ├── cache.php       # 缓存配置
│   ├── queue.php       # 队列配置
│   └── redis.php       # Redis配置
├── public/             # 静态资源
├── extend/             # 扩展类库
└── route/             # 路由定义

4. 数据库设计

创建秒杀活动表:

CREATE TABLE `seckill_activity` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL COMMENT '活动名称',
  `product_id` int(11) NOT NULL COMMENT '商品ID',
  `start_time` datetime NOT NULL COMMENT '开始时间',
  `end_time` datetime NOT NULL COMMENT '结束时间',
  `stock` int(11) NOT NULL COMMENT '库存数量',
  `seckill_price` decimal(10,2) NOT NULL COMMENT '秒杀价格',
  `status` tinyint(1) DEFAULT '0' COMMENT '状态:0未开始 1进行中 2已结束',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `seckill_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `activity_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `order_no` varchar(50) NOT NULL COMMENT '订单编号',
  `create_time` datetime NOT NULL,
  `status` tinyint(1) DEFAULT '0' COMMENT '0未支付 1已支付 2已取消',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_order_no` (`order_no`),
  KEY `idx_user_activity` (`user_id`,`activity_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

三、核心功能实现

1. Redis库存预热

创建服务类 app/service/SeckillService.php:

<?php
namespace appservice;

use thinkfacadeCache;
use appmodelSeckillActivity;

class SeckillService
{
    // 预热活动库存到Redis
    public static function warmUpStock($activityId)
    {
        $activity = SeckillActivity::find($activityId);
        if (!$activity) {
            throw new Exception('活动不存在');
        }
        
        $key = self::getStockKey($activityId);
        Cache::store('redis')->set($key, $activity->stock);
        
        return true;
    }
    
    // 获取库存键名
    public static function getStockKey($activityId)
    {
        return 'seckill_stock:' . $activityId;
    }
    
    // 获取活动锁键名
    public static function getLockKey($activityId, $userId)
    {
        return 'seckill_lock:' . $activityId . ':' . $userId;
    }
}

2. 秒杀核心逻辑

创建控制器 app/controller/Seckill.php:

<?php
namespace appcontroller;

use thinkfacadeCache;
use thinkfacadeQueue;
use appserviceSeckillService;
use appjobCreateSeckillOrder;

class Seckill
{
    // 秒杀接口
    public function seckill($activityId)
    {
        $userId = session('user_id'); // 实际项目中从token获取
        
        // 1. 验证活动状态
        $activity = appmodelSeckillActivity::where('id', $activityId)
            ->where('status', 1)
            ->where('start_time', 'where('end_time', '>=', date('Y-m-d H:i:s'))
            ->find();
            
        if (!$activity) {
            return json(['code' => 400, 'msg' => '活动不存在或已结束']);
        }
        
        // 2. 获取分布式锁 (防止用户重复提交)
        $lockKey = SeckillService::getLockKey($activityId, $userId);
        $lock = Cache::store('redis')->set($lockKey, 1, ['nx', 'ex' => 60]);
        if (!$lock) {
            return json(['code' => 400, 'msg' => '请勿重复提交']);
        }
        
        try {
            // 3. Redis原子操作减库存
            $stockKey = SeckillService::getStockKey($activityId);
            $leftStock = Cache::store('redis')->lua('
                local stock = tonumber(redis.call("GET", KEYS[1]))
                if stock > 0 then
                    redis.call("DECR", KEYS[1])
                    return stock - 1
                else
                    return -1
                end
            ', [$stockKey], 1);
            
            if ($leftStock < 0) {
                return json(['code' => 400, 'msg' => '商品已售罄']);
            }
            
            // 4. 加入订单队列
            $data = [
                'activity_id' => $activityId,
                'user_id' => $userId,
                'seckill_price' => $activity->seckill_price
            ];
            
            Queue::push(CreateSeckillOrder::class, $data);
            
            return json(['code' => 200, 'msg' => '秒杀成功,请尽快支付']);
        } finally {
            // 释放锁
            Cache::store('redis')->delete($lockKey);
        }
    }
}

3. 异步订单处理

创建队列任务 app/job/CreateSeckillOrder.php:

<?php
namespace appjob;

use thinkqueueJob;
use appmodelSeckillOrder;
use appmodelSeckillActivity;

class CreateSeckillOrder
{
    public function fire(Job $job, $data)
    {
        // 1. 检查活动是否还有库存
        $activity = SeckillActivity::find($data['activity_id']);
        if (!$activity || $activity->status != 1 || $activity->stock <= 0) {
            $job->delete();
            return;
        }
        
        // 2. 检查用户是否已有订单
        $existOrder = SeckillOrder::where('user_id', $data['user_id'])
            ->where('activity_id', $data['activity_id'])
            ->find();
            
        if ($existOrder) {
            $job->delete();
            return;
        }
        
        // 3. 创建订单
        $order = new SeckillOrder();
        $order->activity_id = $data['activity_id'];
        $order->user_id = $data['user_id'];
        $order->order_no = 'SK' . date('YmdHis') . mt_rand(1000, 9999);
        $order->create_time = date('Y-m-d H:i:s');
        $order->save();
        
        // 4. 扣减数据库库存
        SeckillActivity::where('id', $data['activity_id'])
            ->where('stock', '>', 0)
            ->dec('stock')
            ->update();
            
        $job->delete();
    }
}

四、高并发优化方案

1. 页面静态化

使用定时任务生成静态页面:

<?php
namespace appcommand;

use thinkconsoleCommand;
use thinkconsoleInput;
use thinkconsoleOutput;
use thinkfacadeView;

class GenerateStatic extends Command
{
    protected function configure()
    {
        $this->setName('generate:static')
            ->setDescription('生成静态页面');
    }
    
    protected function execute(Input $input, Output $output)
    {
        // 获取所有秒杀活动
        $activities = appmodelSeckillActivity::where('status', 1)
            ->where('start_time', '>', date('Y-m-d H:i:s'))
            ->select();
            
        foreach ($activities as $activity) {
            $html = View::fetch('seckill/detail', ['activity' => $activity]);
            file_put_contents(
                public_path('static/seckill/' . $activity->id . '.html'),
                $html
            );
        }
        
        $output->writeln('静态页面生成完成');
    }
}

2. 接口限流中间件

创建 app/middleware/RateLimit.php:

<?php
namespace appmiddleware;

use thinkfacadeCache;

class RateLimit
{
    public function handle($request, Closure $next, $limit = 100, $expire = 60)
    {
        $key = 'rate_limit:' . $request->pathinfo() . ':' . $request->ip();
        
        $count = Cache::store('redis')->inc($key);
        if ($count === 1) {
            Cache::store('redis')->expire($key, $expire);
        }
        
        if ($count > $limit) {
            return json(['code' => 429, 'msg' => '请求过于频繁']);
        }
        
        return $next($request);
    }
}

3. 库存预热命令

创建 app/command/WarmUpStock.php:

<?php
namespace appcommand;

use thinkconsoleCommand;
use thinkconsoleInput;
use thinkconsoleOutput;
use appserviceSeckillService;

class WarmUpStock extends Command
{
    protected function configure()
    {
        $this->setName('warmup:stock')
            ->setDescription('预热秒杀库存到Redis');
    }
    
    protected function execute(Input $input, Output $output)
    {
        $activities = appmodelSeckillActivity::where('status', 1)
            ->where('start_time', '<=', date('Y-m-d H:i:s', time() + 3600)) // 1小时内开始的活动
            ->where('end_time', '>=', date('Y-m-d H:i:s'))
            ->select();
            
        foreach ($activities as $activity) {
            SeckillService::warmUpStock($activity->id);
            $output->writeln("活动{$activity->id}库存预热完成");
        }
        
        $output->writeln('所有活动库存预热完成');
    }
}

五、系统测试与监控

1. JMeter压力测试

创建测试计划:

  1. 线程组设置:1000线程,10秒内启动
  2. HTTP请求:秒杀接口地址
  3. 添加Header:用户token
  4. 添加结果树和聚合报告

2. Redis监控命令

# 监控Redis内存使用
redis-cli info memory

# 监控Redis命令统计
redis-cli info stats

# 监控Redis键空间
redis-cli info keyspace

3. 系统监控指标

  • QPS(每秒查询率)
  • 平均响应时间
  • 错误率
  • Redis内存使用率
  • MySQL活跃连接数

六、生产环境部署

1. Nginx配置优化

# 限制单个IP连接数
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn perip 100;

# 静态文件缓存
location ~* .(html)$ {
    expires 1h;
    add_header Cache-Control public;
}

# PHP请求转发
location ~ .php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    
    # 限制请求体大小
    client_max_body_size 10m;
    
    # 限制超时时间
    fastcgi_read_timeout 300;
}

2. 定时任务配置

# 每分钟检查活动状态
* * * * * cd /path/to/project && php think check:activity

# 每小时生成静态页面
0 * * * * cd /path/to/project && php think generate:static

# 秒杀开始前预热库存
*/5 * * * * cd /path/to/project && php think warmup:stock

七、总结与扩展

本教程详细介绍了使用ThinkPHP 6.0开发高并发秒杀系统的完整流程:

  1. 基于Redis实现库存原子操作
  2. 使用消息队列异步处理订单
  3. 分布式锁防止重复提交
  4. 页面静态化减少服务器压力
  5. 接口限流保护系统稳定

进一步扩展方向:

  • 增加秒杀结果轮询接口
  • 实现秒杀商品预热页面
  • 接入支付系统完成订单
  • 开发后台管理系统
  • 实现分布式部署方案

完整项目代码已上传GitHub:https://github.com/example/tp6-seckill

ThinkPHP 6.0高并发秒杀系统开发全攻略 | 电商系统实战教程
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 thinkphp ThinkPHP 6.0高并发秒杀系统开发全攻略 | 电商系统实战教程 https://www.taomawang.com/server/thinkphp/807.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务