284 lines
8.5 KiB
Markdown
284 lines
8.5 KiB
Markdown
# 017-architecture.mdc (Deep Reference)
|
||
|
||
> 该文件为原始详细规范归档,供 Tier 3 按需读取。
|
||
|
||
---
|
||
|
||
|
||
# 🏗️ Million-Level Concurrency Architecture Standards
|
||
|
||
## 架构总览
|
||
|
||
```
|
||
┌──────────────┐
|
||
│ CDN/WAF │
|
||
└──────┬───────┘
|
||
│
|
||
┌────────────┴────────────┐
|
||
│ Nginx Cluster │
|
||
│ (负载均衡 + SSL + 静态) │
|
||
└────────────┬────────────┘
|
||
│
|
||
┌─────────────────┼─────────────────┐
|
||
▼ ▼ ▼
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ Hyperf-1 │ │ Hyperf-2 │ │ Hyperf-N │
|
||
│ Swoole │ │ Swoole │ │ Swoole │
|
||
│ HTTP:9501│ │ HTTP:9501│ │ HTTP:9501│
|
||
│ WS:9502 │ │ WS:9502 │ │ WS:9502 │
|
||
└────┬─────┘ └────┬─────┘ └────┬─────┘
|
||
│ │ │
|
||
└───────┬───────┴───────┬───────┘
|
||
│ │
|
||
┌──────┴──────┐ ┌─────┴──────┐
|
||
│ Redis │ │ MySQL │
|
||
│ Cluster/ │ │ Master │
|
||
│ Sentinel │ │ ├─Slave 1 │
|
||
│ │ │ └─Slave 2 │
|
||
└─────────────┘ └────────────┘
|
||
```
|
||
|
||
## 无状态服务设计
|
||
|
||
所有 Hyperf 实例必须无状态,确保水平扩展:
|
||
|
||
| 状态类型 | 存储位置 | 禁止 |
|
||
|---------|---------|------|
|
||
| 用户 Session | Redis | 存储在内存/文件 |
|
||
| JWT Token | Redis (可验证+可吊销) | 仅本地验证 |
|
||
| 文件上传 | 对象存储 (S3/OSS) | 本地 storage/ |
|
||
| WebSocket 连接 | Redis 维护映射表 | 进程内变量 |
|
||
| 配置 | 配置中心 / env | 硬编码 |
|
||
|
||
## 缓存策略
|
||
|
||
### 多级缓存
|
||
|
||
```
|
||
L1: 进程内缓存 (APCu / Swoole Table) — 毫秒级, 容量小
|
||
L2: Redis 缓存 — 亚毫秒级, 容量中
|
||
L3: MySQL 查询 — 毫秒级, 容量大
|
||
```
|
||
|
||
### 缓存防护
|
||
|
||
| 问题 | 场景 | 解决方案 |
|
||
|------|------|---------|
|
||
| **穿透** | 查询不存在的数据 | 布隆过滤器 / 缓存空值 (TTL 30s) |
|
||
| **雪崩** | 大量缓存同时过期 | TTL 加随机抖动 / 预热 |
|
||
| **击穿** | 热点 Key 过期时大量请求 | 互斥锁 (singleflight) |
|
||
|
||
```php
|
||
// Cache-Aside with mutex lock
|
||
public function getOrderWithLock(int $id): ?ProductionOrder
|
||
{
|
||
$cacheKey = "order:{$id}";
|
||
$lockKey = "lock:order:{$id}";
|
||
|
||
$cached = $this->redis->get($cacheKey);
|
||
if ($cached !== false) {
|
||
return unserialize($cached);
|
||
}
|
||
|
||
// Mutex: only one coroutine queries DB
|
||
$lock = $this->redis->set($lockKey, '1', ['NX', 'EX' => 5]);
|
||
if ($lock) {
|
||
try {
|
||
$order = ProductionOrder::find($id);
|
||
$ttl = 300 + random_int(0, 60); // jitter
|
||
$this->redis->setex($cacheKey, $ttl, serialize($order));
|
||
return $order;
|
||
} finally {
|
||
$this->redis->del($lockKey);
|
||
}
|
||
}
|
||
|
||
// Wait and retry
|
||
Coroutine::sleep(0.05);
|
||
return $this->getOrderWithLock($id);
|
||
}
|
||
```
|
||
|
||
## 限流策略
|
||
|
||
### 令牌桶 (API 级别)
|
||
|
||
```php
|
||
use Hyperf\RateLimit\Annotation\RateLimit;
|
||
|
||
#[RateLimit(create: 1, capacity: 100, limitCallback: [RateLimitHandler::class, 'handle'])]
|
||
public function list(): array
|
||
{
|
||
// 每秒生成 1 个令牌,桶容量 100
|
||
}
|
||
```
|
||
|
||
### 滑动窗口 (用户级别)
|
||
|
||
```php
|
||
// Redis ZSET sliding window
|
||
public function isRateLimited(string $key, int $maxRequests, int $windowSeconds): bool
|
||
{
|
||
$now = microtime(true);
|
||
$windowStart = $now - $windowSeconds;
|
||
|
||
$pipe = $this->redis->pipeline();
|
||
$pipe->zremrangebyscore($key, '-inf', (string) $windowStart);
|
||
$pipe->zadd($key, [(string) $now => $now]);
|
||
$pipe->zcard($key);
|
||
$pipe->expire($key, $windowSeconds);
|
||
$results = $pipe->exec();
|
||
|
||
return $results[2] > $maxRequests;
|
||
}
|
||
```
|
||
|
||
## 消息队列削峰
|
||
|
||
```
|
||
高并发写入:
|
||
Client → API → Redis Queue (缓冲) → Consumer → MySQL
|
||
|
||
适用场景:
|
||
- 订单创建 (削峰)
|
||
- 通知发送 (异步)
|
||
- 日志记录 (解耦)
|
||
- 数据统计 (批处理)
|
||
```
|
||
|
||
## 服务降级与熔断
|
||
|
||
```php
|
||
// 降级策略
|
||
class OrderService
|
||
{
|
||
public function getStatistics(int $orderId): array
|
||
{
|
||
try {
|
||
return $this->doGetStatistics($orderId);
|
||
} catch (\Throwable $e) {
|
||
// Fallback: return cached/default data
|
||
return $this->getCachedStatistics($orderId) ?? self::DEFAULT_STATS;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 熔断器模式
|
||
// Circuit Breaker: CLOSED → OPEN → HALF-OPEN → CLOSED
|
||
// Use hyperf/circuit-breaker component
|
||
```
|
||
|
||
## 数据库扩展策略
|
||
|
||
### 垂直拆分 (按业务域)
|
||
|
||
```
|
||
production_db → 订单、子订单、发货
|
||
permission_db → 用户、角色、权限
|
||
notification_db → 通知、消息
|
||
```
|
||
|
||
### 水平分片 (百万级后)
|
||
|
||
```
|
||
分片键: order_id
|
||
分片策略: order_id % shard_count
|
||
路由层: Hyperf 中间层 或 ProxySQL
|
||
|
||
注意: 跨分片查询需要聚合层
|
||
```
|
||
|
||
## 部署扩展清单
|
||
|
||
| 阶段 | QPS | 架构 |
|
||
|------|-----|------|
|
||
| 起步 | < 1K | 单机 Docker Compose |
|
||
| 成长 | 1K ~ 10K | Nginx + 2~4 Hyperf + MySQL主从 + Redis Sentinel |
|
||
| 规模 | 10K ~ 100K | K8s + HPA + MySQL Cluster + Redis Cluster |
|
||
| 百万 | 100K+ | 微服务拆分 + 消息队列 + 分库分表 + CDN |
|
||
|
||
## 模块通信规范
|
||
|
||
> 与 `019-modular.mdc` 互补,本节侧重**跨服务/跨进程**通信;019 侧重**代码内模块边界**。
|
||
|
||
### 通信方式选择矩阵
|
||
|
||
| 场景 | 推荐方式 | 禁止方式 |
|
||
|------|---------|---------|
|
||
| 同进程同步调用 | 依赖注入 + 接口 | 全局静态方法 |
|
||
| 跨进程异步 | AsyncQueue / RabbitMQ | 轮询 DB |
|
||
| 实时推送 | WebSocket + Redis Pub/Sub | HTTP 长轮询 |
|
||
| 定时任务 | Hyperf Crontab | sleep 循环 |
|
||
| 跨服务数据 | REST API / 共享 Redis | 直连对方 DB |
|
||
|
||
### 前端模块通信规范
|
||
|
||
```
|
||
组件间通信优先级(由低到高耦合):
|
||
1. Props & Emits(父子) — 首选
|
||
2. provide/inject(跨层级) — 适合主题/配置
|
||
3. Pinia Store(状态共享) — 跨模块全局状态
|
||
4. EventBus (mitt) — 非父子一次性事件(慎用)
|
||
5. URL / Query Params — 页面级状态持久化
|
||
```
|
||
|
||
```typescript
|
||
// ✅ 推荐:通过 Pinia 跨模块共享状态
|
||
// stores/notification.ts
|
||
export const useNotificationStore = defineStore('notification', () => {
|
||
const unreadCount = ref(0)
|
||
const messages = ref<Notification[]>([])
|
||
|
||
function addNotification(msg: Notification): void {
|
||
messages.value.unshift(msg)
|
||
unreadCount.value++
|
||
}
|
||
|
||
return { unreadCount, messages, addNotification }
|
||
})
|
||
|
||
// 订单模块使用通知(无直接耦合)
|
||
// views/order/composables/useOrderActions.ts
|
||
const notificationStore = useNotificationStore()
|
||
|
||
async function createOrder(data: OrderCreateForm): Promise<void> {
|
||
const order = await OrderApi.create(data)
|
||
// 通过 Store 通知,不直接调用通知组件
|
||
notificationStore.addNotification({
|
||
type: 'success',
|
||
title: '订单创建成功',
|
||
content: `订单号 ${order.orderNo} 已提交`,
|
||
})
|
||
}
|
||
```
|
||
|
||
### WebSocket 模块通信
|
||
|
||
```typescript
|
||
// src/composables/useWebSocket.ts — 全局单例 WebSocket 管理
|
||
// 所有模块订阅自己关心的消息类型,不感知连接细节
|
||
export const wsClient = useWebSocket()
|
||
|
||
// 订单模块:只监听订单相关消息
|
||
const { on, off } = useWebSocket()
|
||
on('order.status_changed', (payload) => {
|
||
orderStore.updateOrderStatus(payload.orderId, payload.status)
|
||
})
|
||
|
||
// 通知模块:只监听通知消息
|
||
on('notification.new', (payload) => {
|
||
notificationStore.addNotification(payload)
|
||
})
|
||
```
|
||
|
||
## 性能基线
|
||
|
||
| 指标 | 目标值 | 监控方式 |
|
||
|------|--------|---------|
|
||
| API P99 延迟 | < 200ms | Prometheus + Grafana |
|
||
| 数据库查询 | < 50ms | 慢查询日志 |
|
||
| Redis 命令 | < 5ms | Redis INFO |
|
||
| 缓存命中率 | > 85% | 自定义指标 |
|
||
| 错误率 | < 0.1% | Sentry / 日志 |
|
||
| 可用性 | 99.9% | 健康检查 + 告警 |
|