Я работаю над системой java SE (+netty), которая получает от клиентов сообщения разных типов, объединяет их и подталкивает агрегированные результаты в хранилище.
Мне нужно предварительно накапливать сообщения до агрегации до тех пор, пока не будет выполнено одно из двух условий - превышение тайм-аута или превышение количества. Тайм-ауты и количества предварительно настроены для каждого типа и могут сильно различаться. После этого я собираю/уменьшаю сообщения одного и того же типа и отправителя и выталкиваю результат в хранилище. Агрегация может выглядеть как вычисление среднего значения среди сообщений. Или это может быть намного сложнее. Пост-агрегация в хранилище в моем случае неприемлема.
Задача кажется легкой, но я застрял в реализации. Очевидно, мне нужно собирать сообщения в некоторой структуре данных и проверять правила тайм-аута и количества для каждого элемента. Я думал о DelayedQueue<Delayed<List<MyMessages>>>
(List<MyMessages>
- это List<MyMessages>
список сообщений).
DelayedQueue
использует таймауты. Но неясно, как проверять максимальные количества и добавлять новые сообщения в списки эффективно. Я не хочу проверять все списки на каждое новое сообщение, ища правильный. И выглядит небезопасно для добавления данных в элементы Delayed<List>
.
Какие структуры данных/архитектура подходят для системы, которую я пытаюсь создать? Думаю, такая проблема имеет правильное академическое имя и решение, что я должен делать Google?
Игнорируя существующие структуры данных, которые могут здесь помочь, проблема может быть решена двумя способами: либо поток, принимающий сообщения, выполняет проверки и уведомляет поток агрегации, либо поток агрегации должен опросить. Первый подход упрощает проверку пределов, второй подход упрощает тайм-аут.
Я бы предложил объединить оба: Получение потоков отслеживает, сколько элементов было накоплено, и уведомляет об агрегирующем потоке, если порог достигнут, а агрегатный поток отслеживает время.
Вы можете сделать это, упрощенно, примерно так:
final long maxWait = 1000;
final int maxMessages = 10;
final ArrayBlockingQueue<Message> queue;
final Thread aggregator = new Thread()
{
@Override
public void run() {
try {
ArrayList<Message> messages = new ArrayList<>();
while ( true ) {
messages.clear();
queue.drainTo( messages );
// Store messages
this.wait( maxWait );
}
}
catch ( InterruptedException e ) {
// Handle this..
}
}
};
final Thread reciever = new Thread()
{
@Override
public void run() {
Message message; // Get this from network
queue.put( message );
if(queue.size() > maxMessages) {
aggregator.notify();
}
}
}
Это не обрабатывает вашу группу сообщений, но я уверен, что вы можете увидеть, как это можно экстраполировать для обработки нескольких очередей разных типов сообщений. Чтобы агрегатор учитывал только определенный тип сообщения, когда он уведомлялся, вы могли бы использовать более сложный механизм обмена сообщениями, а не ждать/уведомлять, например, ожидают ли очереди в очереди, где получение потоков в свою очередь может помещать очереди как "сообщения" "о очередях, которые необходимо объединить и сохранить.