发布日期:2023年11月
阅读时间:15分钟
一、项目背景与架构设计
随着电商业务的快速发展,传统单体架构已无法满足高并发、高可用的需求。本教程将基于PHP构建一个支持百万级用户的高性能电商系统,重点解决秒杀、分布式事务等核心难题。
技术栈选型
- 核心框架:Laravel 10 + Swoole 4.8
- 微服务治理:Consul + gRPC
- 缓存层:Redis Cluster + Laravel Cache
- 消息队列:RabbitMQ + Laravel Queue
- 数据库:MySQL 8.0(分库分表)+ Elasticsearch
- 监控系统:Prometheus + Grafana
系统架构图
用户请求 → API网关 → 微服务集群 ├── 用户服务 (User Service) ├── 商品服务 (Product Service) ├── 订单服务 (Order Service) ├── 库存服务 (Inventory Service) ├── 支付服务 (Payment Service) └── 推荐服务 (Recommend Service)
二、微服务架构实现
1. 服务注册与发现
使用Consul实现服务注册发现,每个微服务启动时自动注册,下线时自动注销。
<?php
// app/Services/ConsulService.php
namespace AppServices;
class ConsulService
{
private $client;
public function __construct()
{
$this->client = new GuzzleHttpClient([
'base_uri' => config('consul.host')
]);
}
/**
* 服务注册
*/
public function registerService($serviceName, $serviceId, $address, $port)
{
$payload = [
'Name' => $serviceName,
'ID' => $serviceId,
'Address' => $address,
'Port' => $port,
'Check' => [
'HTTP' => "http://{$address}:{$port}/health",
'Interval' => '10s',
'Timeout' => '5s'
]
];
$response = $this->client->put('/v1/agent/service/register', [
'json' => $payload
]);
return $response->getStatusCode() === 200;
}
/**
* 服务发现
*/
public function discoverService($serviceName)
{
$response = $this->client->get("/v1/health/service/{$serviceName}");
$services = json_decode($response->getBody(), true);
$healthyServices = array_filter($services, function($service) {
$checks = $service['Checks'];
return array_reduce($checks, function($carry, $check) {
return $carry && $check['Status'] === 'passing';
}, true);
});
return array_map(function($service) {
return [
'id' => $service['Service']['ID'],
'name' => $service['Service']['Service'],
'address' => $service['Service']['Address'],
'port' => $service['Service']['Port']
];
}, $healthyServices);
}
}
2. gRPC服务通信
使用gRPC实现高性能的微服务间通信,相比HTTP API有更好的性能表现。
// protos/product_service.proto
syntax = "proto3";
package product;
service ProductService {
rpc GetProductDetail(ProductRequest) returns (ProductResponse);
rpc UpdateProductStock(StockUpdateRequest) returns (StockUpdateResponse);
}
message ProductRequest {
int64 product_id = 1;
}
message ProductResponse {
int64 id = 1;
string name = 2;
string description = 3;
double price = 4;
int32 stock = 5;
int32 status = 6;
}
message StockUpdateRequest {
int64 product_id = 1;
int32 quantity = 2;
int32 operation_type = 3; // 1:增加 2:减少
}
message StockUpdateResponse {
bool success = 1;
string message = 2;
int32 current_stock = 3;
}
<?php
// app/Services/ProductGrpcClient.php
namespace AppServices;
class ProductGrpcClient
{
private $client;
public function __construct()
{
$services = app(ConsulService::class)->discoverService('product-service');
if (empty($services)) {
throw new Exception('Product service not available');
}
$service = $services[array_rand($services)];
$target = "{$service['address']}:{$service['port']}";
$this->client = new GrpcProductServiceClient(
$target,
['credentials' => GrpcChannelCredentials::createInsecure()]
);
}
public function getProductDetail($productId)
{
$request = new GrpcProductRequest();
$request->setProductId($productId);
list($response, $status) = $this->client->GetProductDetail($request)->wait();
if ($status->code !== GrpcSTATUS_OK) {
throw new Exception("gRPC call failed: {$status->details}");
}
return [
'id' => $response->getId(),
'name' => $response->getName(),
'price' => $response->getPrice(),
'stock' => $response->getStock()
];
}
}
三、秒杀系统核心实现
1. 秒杀架构设计
秒杀场景的核心挑战在于高并发下的库存超卖和系统过载问题。我们采用分层过滤和异步处理的策略。
- 第一层:CDN静态化 – 商品详情页静态资源缓存
- 第二层:Redis缓存 – 库存预扣减和用户频率限制
- 第三层:消息队列 – 异步处理订单创建
- 第四层:数据库 – 最终一致性保证
2. Redis库存预扣减
<?php
// app/Services/SeckillService.php
namespace AppServices;
use IlluminateSupportFacadesRedis;
class SeckillService
{
const SECKILL_PREFIX = 'seckill:';
const USER_LIMIT_PREFIX = 'user_limit:';
/**
* 秒杀请求处理
*/
public function processSeckillRequest($userId, $productId, $quantity = 1)
{
$productKey = self::SECKILL_PREFIX . $productId;
$userLimitKey = self::USER_LIMIT_PREFIX . $productId . ':' . $userId;
// 1. 检查用户购买频率限制
$userPurchaseCount = Redis::get($userLimitKey);
if ($userPurchaseCount && $userPurchaseCount >= 1) {
throw new Exception('您已经参与过本次秒杀活动');
}
// 2. Lua脚本原子操作:检查库存并预扣减
$luaScript = <<<LUA
local stockKey = KEYS[1]
local stock = redis.call('get', stockKey)
if not stock then
return -1 -- 商品不存在
end
if tonumber(stock) < tonumber(ARGV[1]) then
return -2 -- 库存不足
end
local newStock = redis.call('decrby', stockKey, ARGV[1])
return newStock
LUA;
$result = Redis::eval($luaScript, 1, $productKey, $quantity);
if ($result === -1) {
throw new Exception('秒杀商品不存在');
}
if ($result === -2) {
throw new Exception('商品已售罄');
}
if ($result < 0) {
// 库存不足,恢复预扣减
Redis::incrby($productKey, $quantity);
throw new Exception('库存不足');
}
// 3. 设置用户购买标记
Redis::setex($userLimitKey, 3600, 1); // 1小时内不能重复购买
// 4. 发送消息到队列异步创建订单
event(new SeckillOrderEvent($userId, $productId, $quantity));
return true;
}
/**
* 初始化秒杀库存
*/
public function initSeckillStock($productId, $stock)
{
$key = self::SECKILL_PREFIX . $productId;
Redis::setex($key, 86400, $stock); // 24小时有效期
}
}
3. 异步订单处理
<?php
// app/Jobs/ProcessSeckillOrder.php
namespace AppJobs;
use IlluminateBusQueueable;
use IlluminateContractsQueueShouldQueue;
use IlluminateFoundationBusDispatchable;
use IlluminateQueueInteractsWithQueue;
use IlluminateQueueSerializesModels;
class ProcessSeckillOrder implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $userId;
public $productId;
public $quantity;
public $attempts = 3;
public function __construct($userId, $productId, $quantity)
{
$this->userId = $userId;
$this->productId = $productId;
$this->quantity = $quantity;
$this->onQueue('seckill_orders');
}
public function handle()
{
try {
DB::transaction(function () {
// 1. 检查商品状态
$product = Product::find($this->productId);
if (!$product || $product->status !== 1) {
throw new Exception('商品已下架');
}
// 2. 检查实际库存
if ($product->stock < $this->quantity) {
// 库存不足,需要补偿
$this->handleStockShortage();
throw new Exception('库存不足,订单创建失败');
}
// 3. 扣减数据库库存
$affected = Product::where('id', $this->productId)
->where('stock', '>=', $this->quantity)
->decrement('stock', $this->quantity);
if (!$affected) {
throw new Exception('库存扣减失败');
}
// 4. 创建订单
$order = Order::create([
'user_id' => $this->userId,
'product_id' => $this->productId,
'quantity' => $this->quantity,
'total_amount' => $product->price * $this->quantity,
'status' => Order::STATUS_PENDING,
'order_no' => $this->generateOrderNo()
]);
// 5. 发送订单创建成功通知
event(new OrderCreated($order));
});
} catch (Exception $e) {
Log::error("秒杀订单处理失败: {$e->getMessage()}", [
'user_id' => $this->userId,
'product_id' => $this->productId
]);
throw $e;
}
}
private function handleStockShortage()
{
// 库存不足时的补偿逻辑
// 可以发送通知、记录日志、更新统计等
Log::warning('秒杀库存不足', [
'product_id' => $this->productId,
'user_id' => $this->userId
]);
}
private function generateOrderNo()
{
return date('YmdHis') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
}
}
四、性能优化策略
1. Swoole协程优化
使用Swoole替代传统的PHP-FPM,大幅提升并发处理能力。
<?php
// 基于Swoole的HTTP服务器
$http = new SwooleHttpServer("0.0.0.0", 9501);
$http->set([
'worker_num' => swoole_cpu_num() * 2,
'enable_coroutine' => true,
'task_worker_num' => 8,
'task_enable_coroutine' => true,
]);
// 请求处理
$http->on('Request', function ($request, $response) {
// 静态资源直接返回
if (preg_match('/.(js|css|png|jpg|jpeg|gif)$/', $request->server['request_uri'])) {
$response->header('Content-Type', 'text/plain');
$response->end('Static file');
return;
}
// 创建Laravel应用实例
$app = require_once __DIR__.'/bootstrap/app.php';
$kernel = $app->make(IlluminateContractsHttpKernel::class);
// 转换Swoole请求为Laravel请求
$laravelRequest = createLaravelRequest($request);
// 处理请求
$laravelResponse = $kernel->handle($laravelRequest);
// 发送响应
$response->status($laravelResponse->getStatusCode());
foreach ($laravelResponse->headers->all() as $name => $values) {
$response->header($name, implode(', ', $values));
}
$response->end($laravelResponse->getContent());
$kernel->terminate($laravelRequest, $laravelResponse);
});
$http->start();
2. 数据库优化
<?php
// 数据库分库分表策略
class ShardingService
{
const DB_PREFIX = 'shop_';
const TABLE_PREFIX = 'orders_';
public function getShardingConfig($userId)
{
$dbIndex = $userId % 4; // 4个数据库
$tableIndex = floor($userId / 1000000) % 16; // 16张表
return [
'database' => self::DB_PREFIX . $dbIndex,
'table' => self::TABLE_PREFIX . $tableIndex
];
}
public function getShardedConnection($userId)
{
$config = $this->getShardingConfig($userId);
return DB::connection($config['database']);
}
}
// 使用查询优化
class OrderRepository
{
public function getUserOrdersWithOptimization($userId, $page = 1, $pageSize = 20)
{
return DB::table('orders')
->select('id', 'order_no', 'total_amount', 'status', 'created_at')
->where('user_id', $userId)
->where('status', '>', 0) // 避免NULL查询
->orderBy('id', 'desc') // 使用索引排序
->offset(($page - 1) * $pageSize)
->limit($pageSize)
->get();
}
}
五、部署与监控
1. Docker容器化部署
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:1.21
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./logs:/var/log/nginx
depends_on:
- php-fpm
php-fpm:
build: ./php
volumes:
- ./src:/var/www/html
- ./php/php.ini:/usr/local/etc/php/php.ini
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: ${DB_DATABASE}
volumes:
- mysql_data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
redis:
image: redis:6.2-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
consul:
image: consul:1.11
ports:
- "8500:8500"
command: agent -server -bootstrap-expect=1 -ui -client=0.0.0.0
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
2. 性能监控指标
<?php
// 自定义性能监控
class PerformanceMonitor
{
public static function recordRequest($route, $startTime)
{
$duration = (microtime(true) - $startTime) * 1000; // 毫秒
// 记录到Prometheus
app('prometheus')->histogramObserve(
'http_request_duration_milliseconds',
$duration,
['route' => $route]
);
// 记录慢查询
if ($duration > 1000) { // 超过1秒
Log::warning('慢请求检测', [
'route' => $route,
'duration' => $duration
]);
}
}
public static function recordDatabaseQuery($query, $duration)
{
app('prometheus')->histogramObserve(
'database_query_duration_milliseconds',
$duration,
['query' => $query]
);
}
}
// 中间件记录请求性能
class PerformanceMiddleware
{
public function handle($request, Closure $next)
{
$startTime = microtime(true);
$response = $next($request);
PerformanceMonitor::recordRequest(
$request->route()->getName() ?? $request->path(),
$startTime
);
return $response;
}
}
六、总结与展望
通过本教程,我们构建了一个基于PHP的高性能电商系统,重点解决了微服务架构、秒杀场景、性能优化等核心问题。系统具备高可用、高并发、易扩展的特点。
关键技术亮点
- 架构设计:微服务架构 + 服务治理
- 并发处理:Redis原子操作 + 消息队列异步化
- 性能优化:Swoole协程 + 数据库分库分表
- 监控保障:全链路监控 + 自动化告警
未来扩展方向
- 引入AI商品推荐算法
- 实现实时数据分析大屏
- 构建移动端原生应用
- 接入区块链溯源系统
- 开发国际化多语言版本