Skip to content

Repository files navigation

🏢 高并发实时预定系统

基于 React + MobX + Material-UI 构建的前端并发处理解决方案,无需WebSocket支持

构建状态 TypeScript React

📚 文档导航

📖 核心文档

文档 描述
README.md 项目索引和快速开始(本文件)

🎯 快速跳转

🎯 快速开始

安装依赖

pnpm install

启动开发服务器

pnpm run dev

访问: http://localhost:5175/

构建生产版本

pnpm run build

✨ 核心特性

特性 描述 状态
并发冲突处理 优雅处理多用户同时预定
乐观更新 UI立即反馈,后端确认
跨页签同步 GlobalEventBus 跨页签数据同步
全局锁机制 防止跨页签并发冲突
锁定超时 30秒自动解锁机制
状态回滚 冲突时自动恢复状态
测试工具 完整的并发测试套件(含跨页签测试)
无需WebSocket 纯前端实现,部署简单

🏗️ 项目架构

┌─────────────────────────────────────────────────────────────┐
│                     Frontend (React)                        │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │   MobX Store │  │ GlobalEvent  │  │   Material-UI    │  │
│  │              │  │     Bus      │  │   Components     │  │
│  └──────────────┘  └──────────────┘  └──────────────────┘  │
├─────────────────────────────────────────────────────────────┤
│                 Mock API Service                            │
│  ┌──────────────────────────────────────────────────────┐  │
│  │     BookingAPI (模拟并发冲突和状态管理)              │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

架构说明:

  • 前端层: React + MobX进行状态管理,GlobalEventBus实现跨页签同步
  • 全局状态层: GlobalEventBus提供跨页签数据同步能力(基于 BroadcastChannel + localStorage)
  • UI层: Material-UI提供响应式组件
  • 服务层: Mock API模拟后端行为和并发冲突
  • 无服务器依赖: 纯前端实现,无需WebSocket或实时通信服务

🌐 跨页签同步机制

问题场景: 多个浏览器标签页同时访问系统,需要避免并发冲突

解决方案:

标签页A: 尝试预定 slot-1 → 获取全局锁 → 成功
标签页B: 尝试预定 slot-1 → 检测到全局锁 → 失败提示

实现方式:

  1. GlobalEventBus: 基于 BroadcastChannel + localStorage 的事件总线
  2. 全局锁管理: 使用 localStorage 存储跨页签锁状态
  3. 锁超时机制: 30秒自动清理过期锁
  4. 事件广播: 锁状态变化实时广播到所有标签页

核心代码:

// 获取全局锁
const acquired = globalEventBus.acquireLock(slotId, userId)
if (!acquired) {
  return { success: false, error: '已被其他页签锁定' }
}

🔄 状态机设计

时间段状态流转:

┌──────────────┐     用户点击预定     ┌──────────────┐
│   AVAILABLE  │ ─────────────────→ │    LOCKED    │
│   (可用)     │                     │   (锁定中)   │
└──────┬───────┘                     └──────┬───────┘
       │                                    │
       │ 预定成功/确认          │ 预定失败/超时 │
       │                      │              │
       ↓                      │              ↓
┌──────────────┐             │        ┌──────────────┐
│   BOOKED     │             │        │   AVAILABLE  │
│  (已预定)     │ ←───────────┘        │   (回滚)     │
└──────────────┘                       └──────────────┘

关键设计点:

  1. 锁定机制: 防止同一时间段被多次预定
  2. 超时机制: 30秒自动解锁,避免死锁
  3. 乐观更新: UI立即响应,后端确认后更新
  4. 状态回滚: 失败时自动恢复到原始状态

📦 分层架构

1. 表现层 (Presentation Layer)

  • React组件渲染
  • Material-UI样式系统
  • 用户交互处理

2. 状态管理层 (State Management Layer)

  • MobX响应式状态管理
  • 乐观更新逻辑
  • 并发冲突检测

3. 服务层 (Service Layer)

  • BookingAPI: 业务逻辑处理
  • GlobalEventBus: 跨页签事件总线
  • 并发控制机制

4. 数据层 (Data Layer)

  • TypeScript类型定义
  • 数据验证和转换
  • 状态持久化(localStorage)

🎯 设计模式

1. 观察者模式 (Observer Pattern)

// GlobalEventBus事件监听
this.globalEventBus.on('lock_acquired', (data) => {
  // 自动更新UI
})

2. 策略模式 (Strategy Pattern)

// 根据不同错误类型采取不同处理策略
switch (error.code) {
  case ApiErrorCode.CONFLICT_DETECTED:
    this.handleConflict()
    break
  case ApiErrorCode.TIMESTAMP_EXPIRED:
    this.handleTimeout()
    break
}

3. 备忘录模式 (Memento Pattern)

// 保存原始状态用于回滚
this.clientState.optimisticUpdates.set(slotId, originalSlot)

📁 目录结构

booking/
├── src/
│   ├── components/              # React组件
│   │   ├── TimeSlotCard.tsx         # 时间段卡片组件
│   │   └── TimeSlotGrid.tsx         # 时间段网格布局
│   ├── stores/                  # MobX状态管理
│   │   └── BookingStore.ts          # 预定状态管理核心
│   ├── services/                # 服务层
│   │   ├── BookingAPI.ts            # API服务(模拟后端)
│   │   └── GlobalEventBus.ts        # 跨页签事件总线
│   ├── types/                   # 类型定义
│   │   └── index.ts                 # 核心TypeScript类型
│   ├── utils/                   # 工具函数
│   │   └── ConcurrencyTester.ts     # 并发测试工具
│   ├── demo/                    # 测试演示
│   │   └── ConcurrencyDemo.tsx      # 交互式测试界面
│   └── App.tsx                  # 主应用入口
├── public/                      # 静态资源
├── dfx.json                     # IC canister配置
├── vite.config.ts               # Vite构建配置
└── README.md                    # 项目索引(本文件)

🎬 使用演示

1. 基础使用

  1. 打开 http://localhost:5175/
  2. 输入用户名并确认
  3. 浏览可用时间段
  4. 点击"立即预定"
  5. 观察状态变化

2. 并发测试

打开多个浏览器窗口,模拟多用户同时预定:

  • 用户A和用户B同时点击同一时间段
  • 观察并发冲突处理
  • 验证状态回滚机制

3. 测试工具

访问 /demo 路径使用交互式测试工具:

  • 并发预定测试
  • 快速预定测试
  • 锁定超时测试
  • 跨页签并发测试 (新增)
  • 全局锁机制测试 (新增)
  • 完整测试套件

4. 跨页签测试演示

打开多个浏览器标签页,访问 http://localhost:5175/demo:

测试步骤:

  1. 在标签页A中点击"跨页签并发预定测试"
  2. 在标签页B中点击"测试全局锁机制"
  3. 观察全局锁状态显示
  4. 验证跨页签数据同步效果

预期结果:

  • 只有第一个请求能获取全局锁
  • 其他请求收到"已被锁定"提示
  • 锁状态实时同步到所有标签页

🔍 核心技术

💡 系统设计原理

1. 防止并发冲突机制

问题场景: 多个用户同时点击同一时间段进行预定

解决方案:

用户A点击 → 锁定时间段 → 发送请求 → 预定成功
用户B点击 → 等待/回滚 → 显示冲突信息

实现代码:

// BookingStore中的并发处理逻辑
if (slot.status === SlotStatus.BOOKED) {
  return {
    success: false,
    conflict: {
      bookedBy: slot.bookedBy,
      bookedAt: slot.bookedAt
    }
  }
}

2. 跨页签同步机制

核心思想: 使用 GlobalEventBus 管理跨页签锁状态,防止并发冲突

实现代码:

// GlobalEventBus 核心方法
acquireLock(slotId: string, userId: string): boolean {
  const locks = this.getLocks()
  const existing = locks[slotId]

  // 检查是否被其他用户锁定
  if (existing && existing.userId !== userId) {
    return false
  }

  // 获取锁并广播
  locks[slotId] = { userId, lockedAt: new Date().toISOString() }
  localStorage.setItem(this.LOCK_KEY, JSON.stringify(locks))
  this.emit('lock_acquired', { slotId, userId })
  return true
}

关键特性:

  • 30秒锁超时自动清理
  • 实时锁状态广播到所有页签
  • 基于 BroadcastChannel + localStorage 实现

3. 乐观UI更新模式

核心思想: 用户操作立即反映在UI上,后端确认后更新状态

流程图:

1. 用户点击预定
   ↓
2. 立即更新UI为"锁定中"
   ↓
3. 发送请求到后端
   ↓
4. 后端响应:
   ├─ 成功 → 更新为"已预定"
   └─ 失败 → 回滚到"可用"

实现代码:

// 1. 立即更新UI
applyOptimisticUpdate(slotId, optimisticSlot)

// 2. 发送请求
const response = await api.bookSlot(request)

// 3. 确认或回滚
if (response.success) {
  confirmBooking(response.slot)
} else {
  rollbackToOriginal(slotId)
}

4. 智能回滚机制

触发条件:

  • 网络请求失败
  • 并发冲突检测
  • 锁定超时(30秒)

回滚策略:

private handleBookingFailure(slotId: string, response: BookingResponse) {
  const originalSlot = this.clientState.optimisticUpdates.get(slotId)

  if (originalSlot) {
    // 回滚到原始状态
    this.slots.set(slotId, originalSlot)
    this.clearOptimisticUpdate(slotId)
  }
}

超时回滚:

setTimeout(() => {
  const pending = this.clientState.pendingBookings.get(slotId)
  if (pending && pending.status === 'pending') {
    this.handleBookingTimeout(slotId)
  }
}, this.config.maxPendingTime)

乐观更新模式

// 1. 立即更新UI
applyOptimisticUpdate(slotId, optimisticSlot)

// 2. 发送请求
const response = await api.bookSlot(request)

// 3. 确认或回滚
if (response.success) {
  confirmBooking(response.slot)
} else {
  rollbackToOriginal(slotId)
}

并发冲突检测

if (slot.status === SlotStatus.BOOKED) {
  return {
    success: false,
    conflict: {
      bookedBy: slot.bookedBy,
      bookedAt: slot.bookedAt
    }
  }
}

锁定超时机制

setTimeout(() => {
  if (pending.status === 'pending') {
    rollbackToAvailable(slotId)
  }
}, LOCK_TIMEOUT) // 30秒

📊 性能指标

  • 构建大小: ~553KB (gzipped: ~170KB)
  • 首次加载: < 1秒
  • 响应时间: < 100ms
  • 并发支持: 20+ 用户同时预定
  • 内存使用: < 50MB
  • 跨页签同步: 实时(< 50ms延迟)

🛠️ 技术栈

技术 版本 用途
React 19.2 前端框架
MobX 6.15 状态管理
Material-UI 6.5 UI组件库
TypeScript 5.9 类型安全
Vite 7.2 构建工具
date-fns 4.1 日期处理
React Router 7.11 路由管理

🌟 IC Canister 支持

本项目已配置为可部署到 Internet Computer (IC) canister:

部署命令

# 构建项目
npm run build

# 部署到本地网络
dfx deploy my_frontend --network local

# 部署到 IC 主网
dfx deploy my_frontend --network ic

IC 特性

  • ✅ SPA 路由支持
  • ✅ 静态资源优化
  • ✅ 安全头配置
  • ✅ 无需服务器端支持
  • ✅ 纯前端实现

📈 浏览器支持

  • Chrome >= 90
  • Firefox >= 90
  • Safari >= 14
  • Edge >= 90

注意: 需要支持 BroadcastChannel 和 localStorage(现代浏览器均支持)

🎓 学习资源

推荐阅读

相关项目

注意: 这是一个演示项目,展示了高并发场景下的前端解决方案。在生产环境中使用前,请根据实际需求进行调整。

About

High-Concurrency Real-time Booking UI

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages