我应该如何处理 Java MUD 中的持久性? OptimisticLock异常处理
在获得原始开发人员的许可后,我正在用 Java 重新实现一个旧的 BBS MUD 游戏。目前,我使用 Java EE 6 和 EJB 会话外观来实现游戏逻辑,使用 JPA 来实现持久性。我选择会话 Bean 的一个重要原因是 JTA。
我对网络应用程序更有经验,如果您收到 OptimisticLockException,您只需捕获它并告诉用户他们的数据已过时,他们需要重新应用/重新提交。在多用户游戏中始终以“重试”来响应将带来可怕的体验。考虑到我预计战斗中会有几个人瞄准同一个怪物,我认为出现 OptimisticLockException 的可能性会很高。
我的视图代码(显示 telnet CLI 的部分)是 EJB 客户端。我应该捕获 PersistenceExceptions 和 TransactionRolledbackLocalExceptions 并重试吗?你如何决定何时停止?
我应该切换到悲观锁定吗?
在每个用户命令后坚持是不是太过分了?我应该将整个世界加载到 RAM 中并每隔几分钟转储状态吗?
我是否可以将会话外观设置为 EJB 3.1 单例,以充当阻塞点,从而消除执行任何类型的 JPA 锁定的需要? EJB 3.1 单例用作多读取器/单写入器设计(您将方法注释为读取器和写入器)。
基本上,对于应用程序中的高度并发数据更改(如果向用户显示重新提交/重试提示是不可接受的),最佳设计和 Java 持久性 API 是什么?
I'm re-implementing a old BBS MUD game in Java with permission from the original developers. Currently I'm using Java EE 6 with EJB Session facades for the game logic and JPA for the persistence. A big reason I picked session beans is JTA.
I'm more experienced with web apps in which if you get an OptimisticLockException you just catch it and tell the user their data is stale and they need to re-apply/re-submit. Responding with "try again" all the time in a multi-user game would make for a horrible experience. Given that I'd expect several people to be targeting a single monster during a fight I think the chance of an OptimisticLockException would be high.
My view code, the part presenting a telnet CLI, is the EJB client. Should I be catching the PersistenceExceptions and TransactionRolledbackLocalExceptions and just retrying? How do you decide when to stop?
Should I switch to pessimistic locking?
Is persisting after every user command overkill? Should I be loading the entire world in RAM and dumping the state every couple of minutes?
Do I make my session facade a EJB 3.1 singleton which would function as a choke point and therefore eliminating the need to do any type of JPA locking? EJB 3.1 singletons function as a multiple reader/single writer design (you annotate the methods as readers and writers).
Basically, what is the best design and java persistence API for highly concurrent data changes in an application where it is not acceptable to present resubmit/retry prompts to the user?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我忍不住觉得你把本来应该是一个简单的问题过于复杂化了。
商业和成功的 MMO 通常采用这样的方法:
不存在“高度并发”的数据更改,因为每个玩家都将自己的数据保存到数据库的不同行。 (在某些情况下,这实际上是一行 - 一些商业游戏只是根据用户 ID 将玩家数据存储为 Blob。)有时数据有点陈旧,但这并不重要。只有当服务器崩溃时才会出现问题,否则您最终会将当前状态存入数据库。这不是我们谈论的银行间信用转账。
至于MUD和BBS游戏,他们的算法会更简单:
同样,这里没有争用,因为每个玩家都有自己的文件。
在每个用户命令之后坚持确实有点矫枉过正,除非你的游戏是高度货币化的,并且如果人们因服务器崩溃而失去了+3 Axe of Awesome,则存在起诉你的风险。
而除了玩家之外,你还需要坚持什么呢?保留其他所有内容通常会对游戏玩法产生负面影响,因此通常您不想这样做。
至于“并发”访问同一个怪物,根据您的示例,这并不重要。如果你有一个进程,那么怪物应该在 RAM 中。你的单一进程仲裁谁可以击中怪物以及以什么顺序。这不是持久层的工作。处理游戏中的游戏逻辑并持久化结果。
I can't help but feel that you're massively overcomplicating what should be a simple issue.
Commercial and successful MMOs often take an approach like this:
There aren't 'highly concurrent' data changes because each player saves his own data, to different rows of the database. (In some cases, this is literally one row - several commercial games just store the player data as a Blob against the user id.) Sometimes the data is a bit stale, but that's unimportant. It'll only be a problem if the server crashes because otherwise you'll eventually get the current state into the DB. This isn't inter-bank credit transfers we're talking about.
As for MUDs and BBS games, their algorithm would have been simpler still:
Again, there is no contention here, because each player has their own file.
Persisting after every user command is indeed overkill, unless your game is highly monetized and there's a risk of people suing you if they lose their +3 Axe of Awesome due to a server crash.
And what else in the world do you need to persist other than the players? There are often negative gameplay implications for persisting everything else, so usually you don't want to do this.
As for 'concurrent' access to the same monster, as per your example, it doesn't matter. If you have a single process, the monster should be in RAM. Your single process arbitrates who gets to hit the monster and in what order. This isn't a job for your persistence layer. Handle the game logic in the game and persist the results.
如果我没记错的话,许多经典的 MUD 确实将整个世界加载到 RAM 中并定期转储状态。当然,我的记忆是从 16MB 进程大小被认为是可怕的那一天开始的。
If I remember right, many of the classic MUDs did indeed load the entire world in RAM and dump the state periodically. Of course, my memories are from the day that a 16MB process size was considered terrifying.