我正在为我的世界服务器开发一个插件,在那里你可以将redstone连接到互联网。该插件的工作原理是使用游戏中内置的讲台,能够发送/接收任意redstone信号。它使用MQTT协议与互联网通信。
目前,我有一个名为LecternHandlers的讲台加载器,它为创建的每个讲台加载一个新的LecternHandler实例。LecternHandler的所有实例都存储在一个ArrayList中,格式为<Location, LecternHandler>的键/值,位置是讲台的位置(此位置同时存储世界和坐标,是Bukkit api的内置功能)。
至于数据存储,我使用MySQL将讲师和播放器设置保存在两个不同的表中。讲台表存储诸如讲台的位置及其id之类的信息。player表存储诸如要连接到的MQTT代理、是否使用tls、身份验证详细信息等信息。
我想要做的是能够更新播放器设置,并在飞行中卸载和重新加载讲台。目前,我必须遍历每个LecternHandler并检查它是否属于播放器,然后卸载并重新加载它。然后,LecternHandler将从MySQL检索新设置。
我相信这会导致性能问题,如果我一次循环通过数千甚至数万个讲台,特别是如果玩家自己有数千个讲台。
如何解决潜在的性能问题?
对于那些感兴趣的人,如果你想看看代码本身,可以在GPL-3下使用my project is on Github。
我的目标是让这个插件在处理大量讲台时非常高效,这样人们就不会因为一个他们想要的整洁的功能而使他们的服务器变慢。
发布于 2020-04-22 11:21:37
我花了一些时间集思广益,研究如何高效地迭代和更新一个类的数千个实例。
首先,我需要异步处理类,这样当我对大量数据执行繁重的操作时,它不会延迟主线程。Thanks to this comment by @user提醒我可以编写多线程代码。
然后,我需要创建另一个arraylist来处理引用某个特定玩家拥有的讲师。数组列表看起来像<UUID, LecternHandler[]>,有一个我可以遍历的LecternHandler实例的数组列表。这将有助于我只遍历LecternHandler实例的一个子部分,而不是整个列表。这一点很重要,因为每个LecternHandler都有它自己处理的MQTT客户端(它自己异步运行,但我需要它比这更好)。
我可以看到如何创建一个播放器设置数据类,然后该数据类将被属于该播放器的每个LecternHandler引用。LecternHandler的所有实例都将包含对播放器设置的引用,当它被重新加载时,它将从这些设置中读取。我可能会暂缓这一点,因为我不确定如何使这个线程安全,如果它还不安全。
LecternHandler本身应该是异步更新的,因为它不依赖于Bukkit api,除非从MQTT接收数据来更新讲台所在的当前页面(当前页面在1-15的范围内更改redstone的输出,对于那些不知道的人来说,redstone是我的世界的电力版本)。
我看到的这个设置的唯一潜在问题(它已经存在于我当前的设置中)是由于加载一个类的数千个实例(如果不是数万个实例)而导致的潜在的ram使用,然而,这超出了这个问题的范围。
这将允许LecternHandler的实例能够批量或分块地更新,当前的jvm可以处理它们,以防止延迟或冻结问题,因为它不会浪费主线程的cpu周期。
https://stackoverflow.com/questions/61350256
复制相似问题