Нужен элегантный способ вызова произвольного кода на заданном интервале - PullRequest
2 голосов
/ 30 марта 2009

Хорошо, у меня есть игровой сервер, работающий на Java / Hibernate / Spring / Quartz. Часы игры тикают с кварцевым таймером, и это прекрасно работает.

Однако у меня есть много других вещей, которые должны происходить через определенные, настраиваемые интервалы (во время игры, а не в реальном времени).

Например, каждые 24 часа игрового времени (~ 47 минут в режиме реального времени, в зависимости от множителя часов сервера) происходит множество различных ежедневных игровых действий, таких как пополнение запасов или что у вас есть.

Теперь, текущая система довольно грубая, но работает - у меня есть таблица в базе данных, которая по сути является cron - строковый ключ, время выполнения следующего события, а затем часы, минуты, секунды и дни до следующего один после этого. Временной тикер проверяет это, а затем запускает сообщение с этим кодом (строковый ключ событий) в очереди, добавляя дни, минуты, секунды к текущему времени и устанавливая его в качестве следующего времени выполнения.

Слушатель сообщений - это громкая часть - он включает ключ и нажимает один из его методов.

Теперь я понимаю, что это может работать просто отлично, но на самом деле это не устраивает меня. Каким было бы ваше решение, чтобы каждый кусок кода имел свой маленький класс? Какой шаблон дизайна покрывает это? (Я уверен, что есть один). У меня есть несколько идей, но я хотел бы услышать некоторые мнения.

Ответы [ 5 ]

1 голос
/ 31 марта 2009

Вместо включения ключа вы можете использовать хеш-таблицу для отправки этих событий. Таким образом, ваши события таймера не должны знать друг о друге.

Должно быть возможно иметь что-то вроде:

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

Таким образом, вы можете перезапустить события обработки Java-кода, не теряя свою постоянную очередь событий.

http://en.wikipedia.org/wiki/Priority_queue'>Priority очереди стоит посмотреть, если вы еще этого не сделали.

1 голос
/ 30 марта 2009

Я бы использовал вариант Command Pattern. Я бы расширил шаблон Command для создания класса IIntervalCommand. Он будет иметь свойство interval и свойство CanExecute только для чтения в дополнение к методу Execute.

Затем вы создаете класс CommandList, который содержит список IIntervalCommands. Он будет иметь метод с именем CheckToExecute, который вы передаете ему текущее игровое время. Метод CheckToExecute будет проходить по списку, вызывая CanExecute для каждой команды. CanExecute вернет истину, если истекшее время произошло. Если CanExecute возвращает true, тогда CheckToExecute вызовет метод Execute объекта, реализующего IIntervalCommand.

Затем добавление дополнительных игровых событий является вопросом создания нового класса, реализующего IIntervalClass. Создание объекта и добавление его в IntervalCommandList.

Если обработка события занимает много времени, то команда может запустить обработку как отдельный поток. Он вернет false своему свойству CanExecute, пока поток не вернется, даже если интервал снова пройден. Или он появляется в другом потоке, если интервал снова прошел.

Вы избегаете гигантского описания случая. Вы можете удалить базу данных и настроить параметры при создании объектов. Или сохраните его и используйте как часть фабрики, которая создает все ваши IIntervalCommands.

1 голос
/ 30 марта 2009

Вместо включения набора кодов вы можете использовать этот код в качестве ключа на карте, где значения являются объектами, которые реализуют интерфейс обработчика. Это позволяет вам быть более гибким в добавлении новых типов событий.

Шаблон выглядит примерно так:

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();
  }
}

Вместо явной регистрации обработчиков, вы можете использовать что-то вроде ServiceLoader в Java 6, чтобы добавить новое поведение, просто поместив JAR-файлы в путь к классам.

0 голосов
/ 30 марта 2009

Концептуально я думаю, что вы делаете две вещи;

Во-первых, у вас есть уменьшенная версия time . Пока связь между этим временем и временем настенных часов остается постоянной, я уверен, что просто делегировал бы это масштабирующее поведение одному классу, который имел бы сигнатуры типа

DateTime getFutureTime( VirtualTimeSpan timespan)

Я бы использовал это для сопоставления виртуальных промежутков времени с экземплярами реального времени. После этого вы можете работать в режиме реального времени, что, вероятно, немного упростит задачу, поскольку вы можете использовать стандартные функции планирования.

Вторая часть касается планирования работы для будущего рабочего процесса. Есть несколько основных технологий, работающих с этим; Концептуально я думаю, что JMS - это Java-дедушка многих из них, он определяет понятия, очень похожие на те, которые вы используете, и то, что вам нужно. Я думаю, что взглянуть на JMS хорошо, чтобы увидеть концепции, которые могут вас заинтересовать, он использует селекторы для отправки заданий конкретным работникам, так же, как те, которые вы описываете.

Увы, JMS, похоже, никогда не отвечало требованиям большинства людей. Многие люди находили, что он был слишком тяжелым или реализации слишком глючные. Так что, как правило, люди заканчивали своими технологиями очереди. Но понятия все есть. Вы не можете просто использовать кварц?

0 голосов
/ 30 марта 2009

Лично я бы не поместил это в базу данных, а оставил бы отдельный сервис в фоновом режиме. Тогда мой веб-сервис или веб-приложение будут связываться с этим сервисом через межпроцессное взаимодействие. Не знаю, как это переводится в мир Java.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...