首页 > 数据库 >如何构建高并发抢购系统(PHP+Redis)

如何构建高并发抢购系统(PHP+Redis)

时间:2024-09-04 20:26:15浏览次数:7  
标签:product 请求 Redis redis 并发 抢购 PHP userId stock

为了构建一个高并发抢购系统,并确保系统设计具有良好的可扩展性和维护性,可以结合设计模式进行详细设计。以下是结合设计模式的抢购系统详细设计:

1. 系统概述

抢购系统的目标是处理大量用户的并发抢购请求,确保每个用户能够公平地参与抢购,并且避免超卖。设计一个高效的系统涉及以下关键方面:

  • 请求处理:管理用户请求的接收和处理。
  • 库存管理:管理商品的库存,确保不会超卖。
  • 并发控制:处理高并发请求的情况。
  • 异步处理:异步处理长时间运行的任务,避免阻塞主进程。

2. 设计模式应用

2.1 单例模式 (Singleton)

用途:确保系统中只有一个 Redis 连接实例,提高资源利用率。

实现

<?php

class RedisConnection
{
    private static $instance = null;
    private $redis;

    private function __construct()
    {
        $this->redis = new Predis\Client();
    }

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function getRedis()
    {
        return $this->redis;
    }
}

// 使用示例
$redis = RedisConnection::getInstance()->getRedis();
2.2 工厂模式 (Factory)

用途:创建不同的处理策略,如订单生成、库存减少等。

实现

<?php

interface OrderStrategy
{
    public function handle($userId, $productId);
}

class StockReductionStrategy implements OrderStrategy
{
    public function handle($userId, $productId)
    {
        $redis = RedisConnection::getInstance()->getRedis();
        $stock = $redis->get('product_stock:' . $productId);
        if ($stock > 0) {
            $redis->decr('product_stock:' . $productId);
            echo "用户 $userId 成功抢购商品\n";
        } else {
            echo "库存不足\n";
        }
    }
}

class OrderFactory
{
    public static function create($type)
    {
        switch ($type) {
            case 'stock':
                return new StockReductionStrategy();
            default:
                throw new Exception("Unknown strategy type");
        }
    }
}

// 使用示例
$orderStrategy = OrderFactory::create('stock');
$orderStrategy->handle($userId, $productId);
2.3 观察者模式 (Observer)

用途:处理异步任务,例如库存变化通知。

实现

<?php

interface Observer
{
    public function update($message);
}

class StockObserver implements Observer
{
    public function update($message)
    {
        // 处理库存变化逻辑
        echo "处理库存变化: $message\n";
    }
}

class Subject
{
    private $observers = [];

    public function attach(Observer $observer)
    {
        $this->observers[] = $observer;
    }

    public function notify($message)
    {
        foreach ($this->observers as $observer) {
            $observer->update($message);
        }
    }
}

// 使用示例
$subject = new Subject();
$subject->attach(new StockObserver());
$subject->notify("库存减少通知");
2.4 策略模式 (Strategy)

用途:选择不同的抢购策略,如普通抢购、VIP抢购等。

实现

<?php

interface PurchaseStrategy
{
    public function purchase($userId, $productId);
}

class NormalPurchase implements PurchaseStrategy
{
    public function purchase($userId, $productId)
    {
        // 普通抢购逻辑
        echo "用户 $userId 进行普通抢购商品 $productId\n";
    }
}

class VIPPurchase implements PurchaseStrategy
{
    public function purchase($userId, $productId)
    {
        // VIP抢购逻辑
        echo "用户 $userId 进行VIP抢购商品 $productId\n";
    }
}

class PurchaseContext
{
    private $strategy;

    public function __construct(PurchaseStrategy $strategy)
    {
        $this->strategy = $strategy;
    }

    public function execute($userId, $productId)
    {
        $this->strategy->purchase($userId, $productId);
    }
}

// 使用示例
$strategy = new NormalPurchase();
$context = new PurchaseContext($strategy);
$context->execute($userId, $productId);

3. 实现详细步骤

3.1 请求入队

用户发起抢购请求时,将请求放入 Redis 队列中:

<?php

require 'vendor/autoload.php';

use Predis\Client;

$redis = RedisConnection::getInstance()->getRedis();

$userId = $_POST['user_id'];
$productId = 'product_1';

// 检查库存
$stock = $redis->get('product_stock:' . $productId);
if ($stock > 0) {
    // 将用户请求加入抢购队列
    $redis->rpush('purchase_queue:' . $productId, $userId);
    echo "请求已加入队列";
} else {
    echo "库存不足";
}
3.2 请求处理

后台进程从队列中取出请求并处理:

<?php

require 'vendor/autoload.php';

use Predis\Client;

$redis = RedisConnection::getInstance()->getRedis();

while (true) {
    $userId = $redis->lpop('purchase_queue:product_1');
    if ($userId === null) {
        sleep(1);
        continue;
    }

    // 锁定处理
    $lockKey = 'product_lock:product_1';
    $lock = $redis->setnx($lockKey, time());
    if ($lock) {
        $redis->expire($lockKey, 5);

        $stock = $redis->get('product_stock:product_1');
        if ($stock > 0) {
            $redis->decr('product_stock:product_1');
            echo "用户 $userId 成功抢购商品\n";
        } else {
            echo "库存不足\n";
        }
        $redis->del($lockKey);
    } else {
        echo "请求正在处理中,请稍后再试";
    }
}

4. 性能优化

4.1 请求限流

使用 Redis 的限流功能来防止过度请求:

<?php

$redis = RedisConnection::getInstance()->getRedis();

$rateLimitKey = 'rate_limit:' . $userId;
$requests = $redis->get($rateLimitKey);
if ($requests >= 10) {
    echo "请求频繁,请稍后再试";
    exit;
}
$redis->incr($rateLimitKey);
$redis->expire($rateLimitKey, 60);
4.2 异步处理

将处理逻辑移到后台进程中,避免阻塞主进程:

<?php

$processCommand = "php process_queue.php";
exec($processCommand . " > /dev/null 2>&1 &");
4.3 分布式处理

使用多个处理实例来并发处理请求:

<?php

while (true) {
    // 从队列中取出用户请求
    $userId = $redis->lpop('purchase_queue:product_1');
    if ($userId === null) {
        sleep(1);
        continue;
    }

    // 分布式处理逻辑
    // ...
}

5. 事务处理

在处理抢购请求时,使用 Redis 的事务功能确保操作的原子性:

<?php

$redis = RedisConnection::getInstance()->getRedis();

$redis->multi()
    ->decr('product_stock:product_1')
    ->sadd('processed_users:product_1', $userId)
    ->exec();

总结

通过结合设计模式,可以构建一个高效、可维护的抢购系统。关键设计模式包括:

  • 单例模式:管理 Redis 连接实例。
  • 工厂模式:创建不同的处理策略。
  • 观察者模式:处理异步任务。
  • 策略模式:选择不同的抢购策略。

系统实现需要精细化设计请求处理、库存管理和并发控制,并且在高并发场景下进行性能优化和监控。通过这些措施,能够确保系统在处理千万级别的抢购请求时,保持高效和可靠。

标签:product,请求,Redis,redis,并发,抢购,PHP,userId,stock
From: https://blog.csdn.net/Crazy_shark/article/details/141902948

相关文章

  • Python监控进程资源占用并发送邮件
    项目新特性上线后,有时会因为包含内存泄露的问题导致服务资源被占满,以至于线上服务崩溃。最简单的方式是对服务期CPU/内存占用进行监测和及时预警。python中pyutil包是自动化运维常用的工具库之一,可以进行方便获取服务器CPU/内存及进程等相关信息。安装方法pipinstallpsutil......
  • windows C++ 并行编程-并发和UWP(三)
    控制执行线程Windows运行时使用COM线程模型。在此模型中,根据对象处理其同步的方式,对象被托管在不同的单元中。线程安全对象托管在多线程单元(MTA)中。必须通过单个线程访问的对象托管在单线程单元(STA)中。在具有UI的应用程序中,ASTA(应用程序STA)线程负责发送窗......
  • windows C++ 并行编程-并发和UWP(一)
    本文介绍当在通用Windows运行时(UWP)应用中使用任务类生成基于Windows线程池的异步操作时要谨记的一些要点。异步编程的使用是Windows运行时应用模型中的关键组成部分,因为它能使应用保持对用户输入的响应。可以启动长期运行的任务,而不必阻止UI线程,并且可以在以后接......
  • 从初识Redis到精通Redis,一份Java程序员必备Redis实战文档分享
    本文深入浅出的介绍了Redis的五种数据类型,并通过多个实用示例展示了Redis的用法。除此之外还讲述了Redis的优化方法和扩展方法。一共由三个部分组成,第一部分对Redis进行了介绍,说明了Redis的基本使用方法、它拥有的5种数据结构以及操作5种数据结构的命令,并详解了如何使用R......
  • 【Azure Redis】Redis-CLI连接Redis 6380端口始终遇见 I/O Error
    问题描述使用Redis-cli连接Redis服务,因为工具无法直接支持TLS6380端口连接,所以需要使用stunnel配置TLS/SSL服务。根据文章(LinuxVM使用6380端口(SSL方式)连接AzureRedis(redis-cli&stunnel):https://www.cnblogs.com/lulight/p/14188279.html),配置stunnel后,始终......
  • Java 入门指南:Java 并发编程 —— 并发容器 PriorityBlockingQueue
    BlockingQueueBlockingQueue是Java并发包(java.util.concurrent)中提供的一个阻塞队列接口,它继承自Queue接口。BlockingQueue中的元素采用FIFO的原则,支持多线程环境并发访问,提供了阻塞读取和写入的操作,当前线程在队列满或空的情况下会被阻塞,直到被唤醒或超时。常用的......
  • php基于Vue的在线租房管理系统(源码+文档+调试+讲解)
    收藏关注不迷路!!......
  • C# WebSocket高并发通信阻塞问题
    项目上遇到使用WebSocket超时问题,具体情况是这样的,OTA升级过程中,解压zip文件会有解压进度事件,将解压进度通过进程通信传给另一进程,通信提示超时异常小伙伴堂园发现大文件使用Zip解压,解压进度事件间隔竟然是1ms,简直超大频率啊但是,解压事件超频也不应该通信异常啊,于是我通过1ms定......
  • Windows 安装Redis(图文详解)
    原文链接:https://www.cnblogs.com/smile008/p/16676723.html 一、Redis是什么数据库?RemoteDictionaryServer(Redis)是一个开源的使用ANSIC语言编写、遵守BSD协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的API,是跨......
  • 【Redis】缓存击穿、缓存穿透、缓存雪崩原理以及多种解决方案
    一、前言在SpringCloud微服务集群项目中,客户端的请求首先会经过Nginx,Nginx会将请求反向代理到Gateway网关层,接着才会将请求发送到具体的服务service。在service中如果要查询数据,则会到缓存中查询,如果缓存未命中,再到数据库中查询数据。作为缓存的Redis扛住了系统......