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