需要一种优雅的方式在指定的时间间隔内调用任意代码

发布于 2024-07-16 06:29:17 字数 525 浏览 6 评论 0原文

好的,我有一个运行在 Java/Hibernate/Spring/Quartz 中的游戏服务器。 比赛时钟用石英计时器滴答作响,效果很好。

然而,我还有许多其他事情需要在特定的、可调整的时间间隔内发生(在游戏时间,而不是实时)。

例如,每 24 小时游戏时间(约 47 分钟实时时间,取决于服务器时钟乘数)就会发生一堆不同的每日一次游戏操作,例如补给或其他什么。

现在,当前的系统非常粗糙,但是可以工作 - 我在数据库中有一个表,本质上是一个 cron - 一个字符串键,下一个事件的执行时间,然后是下一个事件之前的小时、分钟、秒和天。 时间指示器会检查这一点,然后将包含该代码(事件字符串键)的消息发送到队列中,将天、分钟、秒添加到当前时间并将其设置为下一个执行时间。

消息监听器是最重要的部分——它打开按键并点击它的方法之一。

现在我明白这可以很好地工作,但它确实不适合我。 您的解决方案是什么,将每段代码放在自己的小类中? 什么设计模式涵盖了这一点? (我确信有一个)。 我有一些想法,但我想听听一些意见。

Ok, I have a game server running in Java/Hibernate/Spring/Quartz. The game clock ticks with a Quartz timer, and that works just fine.

However, I have many other things that need to happen at specific, tweakable intervals (in game time, not real time).

For instance, every 24 hours game time (~ 47 minutes real time, depending on the servers clock multiplier) a bunch of different once-a-day game actions happen, like resupply, or what have you.

Now, the current system is pretty rough, but works - I have a table in the database that's essentially a cron - a string key, the execution time of the next event and then hours, minutes, seconds and days until the next one after that. The time ticker checks that and then fires off a message with that code (the events string key) in it to a queue, adding the days, minutes, seconds to the current time and setting that as the next execution time.

The message listener is the grody part - it switches on the key and hits one of its methods.

Now I understand that this can work just fine, but it really doesn't sit well with me. What would your solution be to this, to have each piece of code in its own little class? What design pattern covers this? (I'm sure there is one). I have a few ideas, but I'd like to hear some opinions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

套路撩心 2024-07-23 06:29:17

您可以使用代码作为映射的键,而不是切换一组代码,其中值是实现处理程序接口的对象。 这使您可以更加灵活地添加新事件类型。

该模式看起来像这样:

private final Map<String, Handler> handlers = new TreeMap<String, Handler>();

public void register(String event, Handler handler) { 
  handlers.put(event, handler); 
}

public void handle(String event) {
  Handler handler = handler.get(event);
  if (handler == null) {
    /* Log or throw an exception for unknown event type. */
  }
  else {
    handler.execute();
  }
}

您可以使用 Java 6 的 ServiceLoader 之类的东西,只需将 JAR 放入类路径即可添加新行为,而不是显式注册处理程序。

Rather than a switching on a set of codes, you could use the code as a key into a map, where the values are objects that implement a handler interface. This allows you to be much more flexible in adding new event types.

The pattern looks something like this:

private final Map<String, Handler> handlers = new TreeMap<String, Handler>();

public void register(String event, Handler handler) { 
  handlers.put(event, handler); 
}

public void handle(String event) {
  Handler handler = handler.get(event);
  if (handler == null) {
    /* Log or throw an exception for unknown event type. */
  }
  else {
    handler.execute();
  }
}

Rather than explicitly registering handlers, you could use something like Java 6's ServiceLoader to add new behaviors just by dropping JARs into the class path.

紫﹏色ふ单纯 2024-07-23 06:29:17

我会使用命令模式的变体。 我将扩展命令模式来创建 IIntervalCommand 类。 除了 Execute 方法之外,它还有一个间隔属性和一个只读 CanExecute 属性。

然后创建一个 CommandList 类来保存 IIntervalCommand 列表。 它有一个名为 CheckToExecute 的方法,您可以将当前游戏时间传递给它。 CheckToExecute 方法将遍历列表,为每个命令调用 CanExecute。 如果经过的时间已到,CanExecute 将返回 true。 如果 CanExecute 返回 true,则 CheckToExecute 将调用实现 IIntervalCommand 的对象的 Execute 方法。

然后添加额外的游戏事件只需创建一个实现 IIntervalClass 的新类即可。 实例化该对象并将其添加到 IntervalCommandList 中。

如果事件的处理非常耗时,则该命令可以将处理作为单独的线程产生。 即使间隔再次过去,它也会向其 CanExecute 属性返回 false,直到线程返回。 或者,如果间隔再次过去,您可以让它从另一个线程中产生。

你避免了巨大的案例陈述。 您可以在实例化对象时消除数据库并设置参数。 或者保留它并将其用作创建所有 IIntervalCommand 的工厂的一部分。

I would use a variant of the Command Pattern. I would extend the Command pattern to make a IIntervalCommand class. It would have a interval property, and a readonly CanExecute property in addition to the Execute method.

Then you create a CommandList Class that holds a list of IIntervalCommands. It would have a method called CheckToExecute that you pass it the current game time. The CheckToExecute method would traverse the list calling CanExecute for each command. CanExecute will return true if the elapsed time has occurred. If CanExecute return true then CheckToExecute will call the Execute Method of the object implementing IIntervalCommand.

Then adding additional game events is a matter of creating a new class implementing IIntervalClass. Instantiating the Object and adding it to the IntervalCommandList.

If the processing of the event is time consuming then the command could spawn the processing as a separate thread. It will return false to it's CanExecute property until the thread returns even if the interval has passed again. Or you have it spawn off another thread if the interval passed again.

You avoid the giant case statement. You could eliminate the database and setup the parameters when you instantiate the objects. Or keep it and use it as part of a factory that creates all your IIntervalCommands.

把时间冻结 2024-07-23 06:29:17

您可以使用哈希表来分派这些事件,而不是打开密钥。 这样你的计时器事件就不需要互相了解。

应该可以有类似的东西:

timerQueue.registerHandler("key",new TimerHandler(){
   // do something timer related  
});

这样您就可以重新启动处理事件的java代码,而不会丢失持久的事件队列。

http://en.wikipedia.org/wiki/Priority_queue'>如果您还没有看过的话,优先级队列值得一看。

Instead of switching on the key you can use a hashtable to dispatch these events. This way your timer events don't need to know about each other.

It should be possible do have something like:

timerQueue.registerHandler("key",new TimerHandler(){
   // do something timer related  
});

This way you can restart java code handling events without losing your persisted queue of events.

http://en.wikipedia.org/wiki/Priority_queue'>Priority queues are worth looking at if you have not already.

旧人九事 2024-07-23 06:29:17

我个人不会将其放入数据库中,而是在后台运行一个单独的服务。 然后我的 Web 服务或 Web 应用程序将通过进程间通信与该服务进行通信。 但不知道这如何转化为 java 世界。

I personally wouldn't put this in the database but rather keep a separate service running in the background. Then my webservice or web application would communicate with this service through interprocess communication. Don't know how this translates into java world though.

又爬满兰若 2024-07-23 06:29:17

从概念上讲,我认为你在做两件事;

首先,您有一个时间的缩放版本。 只要这个时间和挂钟时间之间的关系保持不变,我相当确定我会将这种缩放行为委托给单个类,该类将具有像

DateTime getFutureTime( VirtualTimeSpan timespan)

我使用它来将虚拟时间跨度映射到的 签名实时实例。 此后,您可以实时操作,这可能会稍微简化事情,因为您可以使用标准调度功能。

第二部分涉及未来工作进程的调度工作。 有许多核心技术可以实现这一点; 从概念上讲,我认为 JMS 是其中很多的 java-grand-dad,它定义了概念与您正在使用的和您需要的非常相似。 我认为查看 JMS 可以很好地了解您可能感兴趣的概念,它使用选择器将任务发送给特定的工作人员,就像您描述的那样。

唉,JMS 似乎从来不适合大多数人。 很多人发现它太重量级或者实现有太多问题。 因此,通常人们最终会采用自制的队列技术。 但概念都在那里。 不能只用石英吗?

Conceptually I think you're doing two things;

Firstly you have a scaled version of time. As long as the relationship between this time and wall-clock time remains constant I'm fairly sure I'd just delegate this scaling behavior to a single class, that would have signatures like

DateTime getFutureTime( VirtualTimeSpan timespan)

I'd be using this to map virtual time spans to instances of real-time. Thereafter you can operate in real-time, which probably simplifies things a little since you can the use standard scheduling features.

The second part regards scheduling work for a future worker process. There's a number of core technologies working with this; Conceptually I think JMS is the java-grand-dad of a lot of these, it defines concepts much like the ones you're using and what you need. I think taking a look at JMS is fine for seeing concepts you may find interesting, it uses selectors to send tasks to specific workers, much like the ones you decribe.

Alas, JMS never seemed to fit the bill for most people. A lot of people found it was too heavyweight or the implementations too buggy. So usually people ended up with home made queue technologies. But the concepts are all there. Can't you just use quartz ?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文