131 lines
4.2 KiB
Markdown
131 lines
4.2 KiB
Markdown
# 设备分配并发竞争问题解决方案
|
||
|
||
## 问题描述
|
||
|
||
在游戏平台服务器中发现了设备分配的并发竞争条件问题,导致多个用户同时选区时可能被分配到同一个设备。
|
||
|
||
### 具体表现
|
||
- 两个用户(L789AT98、LPUEL6XM)几乎同时(相差400ms)进行选区操作
|
||
- 都被分配到了相同的设备 `xx1`
|
||
- 由于时序问题,冷却机制未能有效防止重复分配
|
||
|
||
## 根本原因分析
|
||
|
||
1. **并发竞争条件**:设备检查和占用不是原子操作
|
||
2. **选择算法单一**:总是选择第一个可用设备,增加了冲突概率
|
||
3. **时序问题**:冷却机制在设备分配之后才生效
|
||
4. **缺少验证机制**:分配后没有冲突检测和回滚
|
||
|
||
## 解决方案
|
||
|
||
### 1. 原子设备分配机制
|
||
|
||
#### 新增方法:`MemoryMachineCooldownService.tryAllocateDevice()`
|
||
- **功能**:原子方式检查设备状态并立即加入冷却队列
|
||
- **特点**:使用设备级锁确保线程安全
|
||
- **效果**:防止多个请求同时分配同一设备
|
||
|
||
```java
|
||
public boolean tryAllocateDevice(String machineId, String reason, Long linkTaskId)
|
||
```
|
||
|
||
#### 新增方法:`MemoryMachineCooldownService.releaseDeviceAllocation()`
|
||
- **功能**:释放设备分配(用于失败回滚)
|
||
- **验证**:确保只有原分配任务才能释放
|
||
|
||
### 2. 设备分配服务 (DeviceAllocationService)
|
||
|
||
#### 核心功能
|
||
- **负载均衡**:随机打乱设备列表,避免总是选择第一个设备
|
||
- **原子分配**:使用新的原子分配方法
|
||
- **冲突检测**:过滤被其他任务占用的设备
|
||
- **验证机制**:分配后双重检查是否存在冲突
|
||
|
||
#### 关键方法
|
||
```java
|
||
public String allocateDevice(List<String> availableDevices, Long linkTaskId, String reason)
|
||
public boolean validateDeviceAllocation(String deviceId, Long linkTaskId)
|
||
public void releaseDeviceAllocation(String deviceId, Long linkTaskId)
|
||
```
|
||
|
||
### 3. 增强的安全检查
|
||
|
||
#### 多层防护机制
|
||
1. **分配前检查**:过滤冷却期和被占用设备
|
||
2. **原子分配**:确保分配操作的原子性
|
||
3. **分配后验证**:检测是否存在分配冲突
|
||
4. **异常回滚**:失败时自动释放设备分配
|
||
|
||
#### 异常处理改进
|
||
- 脚本调用失败时自动释放设备分配
|
||
- 数据库更新失败时回滚设备状态
|
||
- 冲突检测失败时完整回滚操作
|
||
|
||
## 改进效果
|
||
|
||
### 性能优化
|
||
- **负载均衡**:设备选择更均匀,减少热点设备竞争
|
||
- **并发安全**:使用设备级锁,避免全局锁竞争
|
||
- **快速失败**:原子检查快速排除不可用设备
|
||
|
||
### 可靠性提升
|
||
- **原子操作**:消除设备分配的竞争条件
|
||
- **冲突检测**:多层验证确保分配唯一性
|
||
- **自动回滚**:异常情况下自动恢复一致状态
|
||
|
||
### 可维护性增强
|
||
- **清晰日志**:详细记录分配过程和冲突检测
|
||
- **模块化设计**:设备分配逻辑独立为专门服务
|
||
- **故障恢复**:支持手动释放设备分配
|
||
|
||
## 使用场景
|
||
|
||
### 首次选区
|
||
1. 获取空闲设备列表
|
||
2. 调用 `DeviceAllocationService.allocateDevice()` 进行原子分配
|
||
3. 进行脚本调用和数据库更新
|
||
4. 验证分配结果,失败时自动回滚
|
||
|
||
### 重复选区
|
||
- 复用已分配设备,不重复分配
|
||
- 验证设备是否仍然可用
|
||
|
||
## 测试验证
|
||
|
||
### 并发测试
|
||
- 模拟多个用户同时选区
|
||
- 验证不会出现设备重复分配
|
||
- 检查冲突检测和回滚机制
|
||
|
||
### 异常测试
|
||
- 脚本调用失败场景
|
||
- 数据库更新失败场景
|
||
- 验证回滚机制有效性
|
||
|
||
## 监控建议
|
||
|
||
### 关键指标
|
||
- 设备分配成功率
|
||
- 冲突检测次数
|
||
- 回滚操作频率
|
||
- 设备利用率分布
|
||
|
||
### 告警设置
|
||
- 设备分配冲突告警
|
||
- 回滚操作频繁告警
|
||
- 设备池不足告警
|
||
|
||
## 注意事项
|
||
|
||
1. **向后兼容**:保留了原有的冷却机制
|
||
2. **性能影响**:增加了验证步骤,但提升了可靠性
|
||
3. **内存管理**:定期清理过期的冷却记录
|
||
4. **并发限制**:基于内存的锁机制,适用于单实例部署
|
||
|
||
## 后续优化建议
|
||
|
||
1. **分布式锁**:如需多实例部署,考虑使用Redis分布式锁
|
||
2. **设备预分配**:预先为用户分配设备,减少实时分配压力
|
||
3. **智能调度**:基于历史数据和设备负载进行智能分配
|
||
4. **故障转移**:设备故障时自动切换到备用设备
|