PHP项目Symfony lock与资源竞争

wen PHP项目 2

本文目录导读:

PHP项目Symfony lock与资源竞争

  1. Symfony Lock组件的作用
  2. 核心概念
  3. 实际应用场景
  4. 存储后端选择
  5. 高级功能
  6. 最佳实践
  7. 完整示例:高并发订单系统

在Symfony项目中处理资源竞争(Race Condition)是一个常见的挑战,特别是在高并发场景下,Symfony Lock组件正是为了解决这类问题而设计的,让我详细介绍一下:

Symfony Lock组件的作用

Symfony Lock组件提供了一种机制来同步对共享资源的访问,防止多个进程同时修改同一资源导致的竞态条件。

核心概念

Lock工厂(LockFactory)

use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\RedisStore;
// 创建存储(使用Redis)
$redis = new \Redis();
$redis->connect('127.0.0.1');
$store = new RedisStore($redis);
$factory = new LockFactory($store);

获取锁(Lock对象)

// 创建一个锁
$lock = $factory->createLock('resource-key');
// 尝试获取锁(阻塞等待)
$lock->acquire();
// 或者尝试获取锁(非阻塞)
if ($lock->acquire(true)) {
    // 获取到锁,执行操作
}
// 释放锁
$lock->release();

实际应用场景

场景1:防止重复订单

class OrderService
{
    private LockFactory $lockFactory;
    public function createOrder(int $userId, array $products): Order
    {
        // 使用用户ID作为锁资源
        $lock = $this->lockFactory->createLock("order-user-{$userId}");
        try {
            // 尝试获取锁,等待5秒
            if (!$lock->acquire(true)) {
                throw new \RuntimeException('请稍后再试');
            }
            // 业务逻辑
            $order = new Order();
            $order->setUser($userId);
            // ... 处理订单
            return $order;
        } finally {
            $lock->release();
        }
    }
}

场景2:库存扣减

class InventoryService
{
    private LockFactory $lockFactory;
    private EntityManagerInterface $em;
    public function deductStock(int $productId, int $quantity): bool
    {
        $lock = $this->lockFactory->createLock("stock-{$productId}");
        try {
            // 等待最多10秒
            if (!$lock->acquire(true, 10)) {
                throw new LockAcquiringException('库存操作繁忙');
            }
            // 检查库存
            $product = $this->em->getRepository(Product::class)->find($productId);
            if ($product->getStock() < $quantity) {
                return false;
            }
            // 扣减库存
            $product->setStock($product->getStock() - $quantity);
            $this->em->flush();
            return true;
        } finally {
            $lock->release();
        }
    }
}

存储后端选择

Redis(推荐用于生产环境)

composer require symfony/lock symfony/redis
use Symfony\Component\Lock\Store\RedisStore;
$redis = new \Redis();
$redis->connect('redis:6379');
$store = new RedisStore($redis);

Memcached

use Symfony\Component\Lock\Store\MemcachedStore;
$memcached = new \Memcached();
$memcached->addServer('localhost', 11211);
$store = new MemcachedStore($memcached);

文件系统(开发环境)

use Symfony\Component\Lock\Store\FlockStore;
$store = new FlockStore('/tmp/locks');

数据库

use Symfony\Component\Lock\Store\PdoStore;
use Symfony\Component\Lock\Store\PostgreSqlStore;
$store = new PdoStore('mysql:host=localhost;dbname=test');
// 或 PostgreSQL
$store = new PostgreSqlStore('pgsql:host=localhost;dbname=test');

高级功能

自动释放(推荐使用)

// 使用acquireAndExecute自动处理
$result = $this->lockFactory
    ->createLock('resource-key')
    ->acquireAndExecute(function () {
        // 自动获取锁、执行操作、释放锁
        return '操作结果';
    });

锁超时

$lock = $factory->createLock('resource-key', 30); // 30秒后自动释放
// 或者设置TTL(生存时间)
$lock->setTtl(10); // 10秒后锁自动过期

读写锁

use Symfony\Component\Lock\SharedLockInterface;
// 读锁(共享)
$readLock = $factory->createLock('resource-key');
$readLock->acquireRead();
// 写锁(独占)
$writeLock = $factory->createLock('resource-key');
$writeLock->acquire();

最佳实践

在Symfony中配置使用

# config/packages/lock.yaml
framework:
    lock:
        resources:
            default: 'redis://localhost:6379'
            orders: 'redis://localhost:6379/orders'
            inventory: 'redis://localhost:6379/inventory'

然后在服务中使用:

use Symfony\Component\Lock\LockFactory;
class OrderService
{
    private LockFactory $orderLockFactory;
    public function __construct(#['LockFactory' => 'orders'] LockFactory $factory)
    {
        $this->orderLockFactory = $factory;
    }
}

处理锁异常

use Symfony\Component\Lock\Exception\LockAcquiringException;
use Symfony\Component\Lock\Exception\LockConflictedException;
try {
    $lock = $factory->createLock('resource-key');
    if (!$lock->acquire()) {
        throw new \RuntimeException('资源被占用');
    }
    // 业务逻辑
} catch (LockConflictedException $e) {
    // 锁冲突
    throw new \RuntimeException('请稍后重试');
} catch (LockAcquiringException $e) {
    // 获取锁失败
    throw new \RuntimeException('系统繁忙');
} finally {
    $lock?->release();
}

性能优化建议

  1. 选择合适的存储后端:Redis或Memcached性能最佳
  2. 合理设置锁粒度:细粒度锁(按商品ID)比粗粒度锁(整个库存系统)更高效
  3. 设置合理的超时时间:避免长时间占用锁
  4. 使用连接池:减少连接开销

完整示例:高并发订单系统

class OrderController
{
    private LockFactory $lockFactory;
    private EntityManagerInterface $em;
    public function placeOrder(int $productId, int $quantity): JsonResponse
    {
        $lock = $this->lockFactory->createLock("product-{$productId}");
        try {
            // 尝试获取锁,最多等待5秒
            if (!$lock->acquire(true, 5)) {
                return new JsonResponse(['error' => '系统繁忙'], 429);
            }
            // 开始事务
            $this->em->beginTransaction();
            // 业务逻辑
            $product = $this->em->find(Product::class, $productId);
            if ($product->getStock() < $quantity) {
                $this->em->rollback();
                return new JsonResponse(['error' => '库存不足'], 400);
            }
            $product->setStock($product->getStock() - $quantity);
            $order = new Order();
            $order->setProduct($product);
            $order->setQuantity($quantity);
            $this->em->persist($order);
            $this->em->flush();
            $this->em->commit();
            return new JsonResponse(['order_id' => $order->getId()]);
        } catch (\Exception $e) {
            $this->em->rollback();
            return new JsonResponse(['error' => '操作失败'], 500);
        } finally {
            $lock->release();
        }
    }
}

通过合理使用Symfony Lock组件,可以有效解决资源竞争问题,确保数据的一致性和完整性。

抱歉,评论功能暂时关闭!