基于AWS API Gateway Websockets的双人游戏回合结束判定方案问询
解决AWS Websocket双人对战游戏的回合结束检测问题
针对你遇到的并发提交时无法检测双方操作完成的问题,核心根源是非原子的读-检查-写流程导致竞态条件,以下是几个实用的解决方案,适配你的DynamoDB数据模型:
方案1:新增游戏状态记录(推荐)
在现有两条玩家记录的基础上,新增一条以游戏为维度的状态记录,用原子更新操作避免竞态:
- 数据结构:
- primaryKey:
1$(同游戏ID) - secondaryKey:
STATUS - 属性:
user1_submitted(布尔,标记玩家1当前回合是否提交)、user2_submitted(布尔)、current_round(整数)
- primaryKey:
- 操作流程:
- 玩家提交操作时,先更新自己的
moves数组(保持现有逻辑) - 调用DynamoDB的
UpdateItem原子更新状态记录:设置当前玩家的_submitted为true,同时利用返回的最新状态检查是否双方都已提交 - 若确认双方都提交,立刻通过Websocket通知回合结束,然后重置状态记录的提交标记,准备下一回合
- 玩家提交操作时,先更新自己的
这种方式把状态集中管理,原子更新彻底避免了并发问题,还能方便扩展回合数、超时检测等功能。
方案2:基于现有模型的事务+条件检查
如果不想新增状态记录,可以用DynamoDB事务结合条件表达式实现:
- 给
moves数组中的每个操作添加回合标识(比如{"round": 3, "move": "attack"}),方便查询当前回合操作 - 玩家提交操作时,执行一个事务:
UpdateItem:追加自己的回合操作到moves数组ConditionCheck:检查对方的moves数组中是否存在当前回合的操作
- 若条件检查通过,说明对方已提交,直接触发回合结束;若不通过,仅完成自己的更新,等对方提交时再触发检查
这个方案改动小,但事务的读写消耗略高,且需要维护回合标识的一致性。
方案3:优化现有DynamoDB Streams方案
你觉得Streams只适合日志,但实际生产中用它做业务触发是完全可行的,关键是处理幂等性:
- 在Lambda中处理Stream事件时,用强一致性读查询游戏的两条记录,检查双方当前回合的操作是否都已提交
- 新增一个
round_processed字段到状态记录(或玩家记录),当确认双方都提交后,先检查该字段是否为false,再触发Websocket通知,最后原子性地将其设为true - 这样即使Stream事件重复推送,也不会重复发送通知
这种方案不用大幅改动现有逻辑,适合快速迭代,Streams的延迟通常在毫秒级,不会影响游戏体验。
方案1的代码示例(Python + boto3)
import boto3 from your_websocket_utils import send_round_end_notification # 假设你有发送Socket通知的工具函数 dynamodb = boto3.resource('dynamodb') game_table = dynamodb.Table('your_game_table_name') def handle_player_move(game_id, player_id, move): # 更新玩家自己的操作记录 game_table.update_item( Key={ 'primaryKey': f"{game_id}$", 'secondaryKey': player_id }, UpdateExpression="SET moves = list_append(moves, :new_move)", ExpressionAttributeValues={":new_move": [{"round": 1, "action": move}]} # 假设当前是第1回合 ) # 原子更新并检查游戏状态 update_response = game_table.update_item( Key={ 'primaryKey': f"{game_id}$", 'secondaryKey': "STATUS" }, UpdateExpression=f"SET {player_id}_submitted = :true", ConditionExpression=f"attribute_exists({player_id}_submitted) = false", # 防止重复提交 ExpressionAttributeValues={":true": True}, ReturnValues="ALL_NEW" ) current_status = update_response['Attributes'] # 检查双方是否都已提交 if current_status.get("user1_submitted") and current_status.get("user2_submitted"): # 通知双方回合结束 send_round_end_notification(game_id, ["user1", "user2"]) # 重置状态,准备下一回合 game_table.update_item( Key={ 'primaryKey': f"{game_id}$", 'secondaryKey': "STATUS" }, UpdateExpression="SET user1_submitted = :false, user2_submitted = :false", ExpressionAttributeValues={":false": False} )
内容的提问来源于stack exchange,提问作者Kob3Bryant
相关产品推荐
相关产品推荐

