初始化
This commit is contained in:
283
.cursor/rules/references/017-architecture-deep.md
Normal file
283
.cursor/rules/references/017-architecture-deep.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# 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% | 健康检查 + 告警 |
|
||||
Reference in New Issue
Block a user