Похоже, что вы описываете сценарий, в котором служба является посредником между источником публикации (сама служба является подписчиком), и служба повторно передает эту информацию N подписчикам, но в соответствии с их расписание.
Таким образом, предполагая, что обновление - это обновление одной позиции, а не какая-то агрегация, такая как скользящее среднее или буферизация (например, только последняя позиция автомобиля каждые 30 секунд, а не все его позиции с последних 30 секунд), тогда вы необходимо сохранить некоторую информацию для каждого подписчика:
- a подписка . Кто такой потребитель? как я могу уведомить об этом? (например, обратный вызов, очередь ответов и т. д.)
- a спецификация . Чего хочет потребитель и когда? (например, каждые 50 тиков)
- состояние
- время с момента последней отправки
- количество обновлений с момента последней отправки
- ...
Поскольку служба получает обновления, для каждого потребителя она должна сравнивать спецификацию с состоянием для каждого обновления из источника; что-то вроде:
if (consumer.Spec.Matches(consumer.State, updateMessage)
SendUpdate(consumer.Subscription.Callback, updateMessage)
Вышеприведенное предполагает, что ваша спецификация непосредственно выполняется службой (т. Е. Потребители находятся в процессе или спецификация была сериализована и может быть десериализована службой. Если это не , то ваша Возможно, spec может представлять DSL (например, разбираемое представление о том, что сервер может быть скомпилирован во что-то, что он может выполнить). Другой подход заключается в представлении спецификации как набора команд. Например,
public enum FrequencyUnit
{
SecondsSinceLastSend,
UpdatesSinceLastSend,
}
public class Frequency
{
public double Value { get; set; }
public FrequencyUnit Unit { get; set; }
}
public class Operator
{
Every, // Unary: e.g. every update; every 10 sec; every 5 updates
Or, // Nary: e.g. every 50 or every 20 sec (whichever's first)
And, // Nary: e.g. 19 messages and 20 sec have passed
// etc.
}
public class UpdateSpec
{
public Frequency[] Frequencies { get; set; }
public Operator Operator { get; set; }
}
Они довольно гибкие и могут быть настроены на внутреннем коде сервера или встроены чтением XML или чего-то еще. Они также могут быть переданы в службу от самого потребителя при регистрации. Например, IService.Register()
может предоставить интерфейс с подпиской и спецификацией.
Последний бит будет масштабируемость. Я описал цикл обслуживания для каждого обновления для потребителей. Это не будет хорошо масштабироваться, поскольку цикл может блокировать получение обновлений из источника или если он асинхронен с источником, по крайней мере, скорее всего, накопит обновления быстрее, чем их обработает.
Стратегия для решения этой проблемы заключается в добавлении внутренней очереди к информации, которую вы ведете для каждого подписчика. Служба после получения обновления ставит его в очередь в каждую внутреннюю очередь. Сервисные задачи (на основе TPL), потоки пула потоков или долгоживущие потоки будут затем исключать из очереди и оценивать обновление, как указано выше. Существует множество возможных вариантов и оптимизаций этого.