如何使用akka管理用户的游戏状态

coo*_*eze 2 scala akka

我试图弄清楚如何使用akka管理用户游戏状态.

游戏状态将持久保存到mysql,这不会改变,因为我们有其他需要此服务.

游戏中发生的任何事情都被视为"事件".

那么你我有一个人可以达到的"水平".完成与其关联的所有"事件"后,将达到一个级别.

所以你有了:

Level
 - event1  e.g. reach a point in the game
 - event2  e.g. pickup a sword
 - event3  e.g. defeat a monster
Run Code Online (Sandbox Code Playgroud)

所以在游戏中有很多级别,以及与级别相关的100个事件.

所以所有"事件"都通过HTTP发送到我的后端,我将事件保存在数据库中.

然后我必须在内存中加载用户游戏配置文件,然后重新计算自发生新事件以来所达到的等级. 注意:这个计算不能在数据库级别完成,因为我在这里写的有点复杂.

我看到的问题是,如果我使用akka,我不能让多个actor为同一个用户处理事件,因为数据可能变得陈旧.

为了清楚起见,所以当一个新事件到来时,我必须在内存中加载游戏配置文件,遍历各个级别并查看是否已经实现了它们,如果有,则更新数据库

e.g. update levels set achieved=true where level_id = 123 and user_id=234
Run Code Online (Sandbox Code Playgroud)

例如,actor1加载配置文件(该用户的所有级别和事件),然后处理刚到达收件箱的新事件.同时,actor2加载配置文件(与actor1相同),然后处理新事件.当它持续更改mysql时,数据将不在sych中.

如果我使用线程,我将不得不在游戏配置文件计算期间锁定并持久保存到数据库.

我怎样才能使用Akka做到这一点并能够并行处理,或者这个场景是不允许的?

Rom*_*man 5

让我们思考如何在没有演员的情况下管理它.因此,简而言之,您有以下问题场景:

  1. 两个(或更多)更新请求同时到达,两者都将修改相同的数据
  2. 两个请求都读取一些稳定的数据状态,然后以自己的方式更新它并持久保存到DB
  3. 首先签入的请求的修改将丢失,更准确地说 - 被后来的请求覆盖.

这是一个经典问题.它至少有两种经典的解决方案:

  1. 乐观锁定
  2. 悲观锁定:通常通过Serializable对事务应用隔离级别来实现.

值得一读这个答案,并对两个世界进行比较.

当您使用Akka时,您可能希望更喜欢更好的并发性和偶然的故障,这些故障很容易恢复.它与阿卡的座右铭相提并论let it crash.

所以,你需要做下面的步骤:

  1. version在表中添加列.它可以是数字或字符串(带哈希).数字是最简单的.
  2. 插入新记录时 - 初始化版本.
  3. 更新记录时 - 检查version值未更改.那么,这是您的更新策略:
    1. 阅读记录及其内容version.
    2. 更新内存中的记录.
    3. 使用条件执行更新查询where rec_id=$id and version=$version.
    4. 如果更新的记录数是1 - 你很好.如果0 - OptimisticLockException像这样扔或smth.
  4. 最后,是时候Akka完成它的工作了:提出适当的监督策略(我会选择类似的东西try again in 1 second).在actor的preRestart方法中,将更新消息返回到actor的邮箱(请参阅Akka文档中的Restart Hooks章节).

使用此策略,即使两个请求尝试一次更新同一记录,其中一个将失败并将立即再次处理.