2016-05-02 4 views
5

У меня есть сценарий производителя-потребителя, где производители производят намного быстрее, чем потребители могут потреблять. Как правило, решение состоит в том, чтобы блокировать производителей, поскольку сценарий производителя/потребителя работает так же быстро, как и самый медленный компонент. Дросселирование или блокирование производителей - не - хорошее решение, потому что наше приложение обеспечивает достаточно времени, чтобы потребители могли догнать их позже.Реализация Java «Tiered Queue» для быстрых Производители, медленные потребители

Вот диаграмма, изображающая полную «фаза» в нашем приложении по сравнению с более общим сценарием:

 Our Application     Common Scenario 
2N +--------+--------+ 
    |PPPPPPPP|oooooooo|           P = Producer 
    |PPPPPPPP|oooooooo|           C = Consumer 
    N +--------+--------+  N +--------+--------+--------+  o = Other Work 
    |CPCPCPCP|CCCCCCCC|  |CPCPCPCP|CPCPCPCP|oooooooo|  N = number of tasks 
    |CPCPCPCP|CCCCCCCC|  |CPCPCPCP|CPCPCPCP|oooooooo| 
    -------------------  ---------------------------- 
    0  T/2  T  0  T/2  T  3T/2 

Идея заключается в том, чтобы максимизировать пропускную способность, не препятствуя производитель.

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

Я использую Java ThreadPoolExecutor с BlockingQueue с максимальной емкостью, чтобы гарантировать, что у нас не закончится память. Проблема заключается в реализации такой «многоуровневой» очереди, где задачи, которые могут быть поставлены в очередь в памяти, выполняются немедленно, в противном случае данные помещаются в очередь на диске.

Я придумал два возможных решения:

  1. Реализовать BlockingQueue с нуля, используя реализацию LinkedBlockingQueue или ArrayBlockingQueue в качестве ссылки. Это может быть так же просто, как копирование реализации в стандартной библиотеке и добавление файловой системы для чтения/записи.
  2. Продолжайте использовать стандартную реализацию BlockingQueue, внесите отдельный файл FilesystemQueue для хранения моих данных и с использованием одного или нескольких потоков для удаления файлов, создайте Runnable s и обнулите их, используя ThreadPoolExecutor.

Есть ли они в разумных пределах и есть ли потенциально лучший подход?

ответ

2

Первый вариант состоит в увеличении доступное пространство кучи размером, а предложилДимитр Димитров, используя флаг памяти -Xmx, например java -Xmx2048m

От Oracle's Documentation: Обратите внимание, что JVM использует больше памяти, чем только кучу. Например, Java-методы, стеки потоков и собственные ручки выделяются в памяти отдельно от кучи, а также внутренние структуры данных JVM.

Здесь также схема, как Java кучи памяти является классифицировать.

enter image description here


Второй вариант заключается в использовании библиотеки, которая реализует функциональные возможности запрашиваемую. Для этой цели можно использовать ashes-queue

Из обзора проекта: Это простая реализация FIFO в Java , которая имеет постоянную поддержку. То есть, если очередь заполнена, переполненные сообщения будут сохраняться, и когда будут доступны слоты , они будут возвращены в память.


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

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

2

Это похоже на идеальную ситуацию для использования очереди JMS, а не файловой системы.

Вместо того, чтобы блокировать очередь, отправляйте сообщения в постоянной JMS-очереди. Вы все же можете попробовать многоуровневый подход, объединив очередь JMS параллельно с BlockingQueue, отправляя в очередь JMS, когда BlockingQueue заполнен, но я уверен, что чистый JMS-подход будет работать сам по себе.

4

Прежде чем перейти к более сложному решению, действительно ли вы уверены в том, что использование ограниченного BlockingQueue - это автоматический выключатель для вас? Может оказаться, что увеличение размера вашей кучи и предварительное распределение достаточно большой емкости все еще в порядке для вас. Это позволит вам избежать сложностей и неопределенности производительности по цене пауз GC, которые находятся в пределах вашей зоны комфорта.

Все еще, если ваша рабочая нагрузка не сбалансирована, что она может воспользоваться преимуществом, сохраняя количество сообщений, которые не могут поместиться в памяти (по сравнению с проверенной очереди блокировки MPMC), похоже, что вам нужна более простая, меньшая версия ActiveMQ или его Apollo off-shoot. В зависимости от вашего приложения вы можете найти полезные функции ActiveMQ, и в этом случае вы можете использовать его напрямую. Если нет, вам, вероятно, лучше искать пространство JMS, так как bowmore предлагает.