Вопрос

Субъект:

Я пытаюсь реализовать базовое планирование заданий на Java для обработки повторяющихся постоянных запланированных заданий (для личного проекта learn).Я не хочу использовать какие-либо (готовые к использованию) библиотеки, такие как Quartz / Obsidian / Cron4J / etc.

Цель:

  • Задание должно быть постоянным (для обработки завершения работы сервера)
  • Время выполнения задания может занять до ~ 2-5 млн.
  • Управление большим объемом работы
  • Многопоточность
  • Легкий и быстрый ;)

Вся моя работа выполняется в базе данных MySQL.

JOB_TABLE (id, name, nextExecution,lastExecution, status(IDLE,PENDING,RUNNING))

Шаг за шагом:

  1. Извлекать каждое задание из “JOB_TABLE” где “nextExecution > now” AND “status = IDLE“.Этот шаг выполняется каждые 10 миллионов одним потоком.

  2. Для каждого извлеченного задания я помещаю новый поток в ThreadPoolExecutor затем я обновляю статус задания до “PENDING” в моем “JOB_TABLE”.

  3. Когда поток заданий запущен, я обновляю статус задания до “RUNNING”.

  4. Когда задание будет завершено, я обновлю lastExecution с учетом текущего времени я устанавливаю новый nextExecution время, и я меняю статус задания на “IDLE”.

Когда сервер запускается, я помещаю каждое ОЖИДАЮЩЕЕ / ЗАПУЩЕННОЕ задание в ThreadPoolExecutor.

Вопрос/Наблюдение:

  • Шаг 2 :Будет ли ThreadPoolExecutor обрабатывать большое количество потоков (~ 20000)?
  • Должен ли я использовать решение NoSQL вместо MySQL?
  • Является ли это лучшим решением для решения такого варианта использования?

Это черновик, за ним нет никакого кода.Я открыт для предложений, комментариев и критики!

Это было полезно?

Решение

Я выполнил аналогичную вашей задачу в реальном проекте, но в .NET.Вот что я могу вспомнить относительно ваших вопросов:

Шаг 2 :Будет ли ThreadPoolExecutor обрабатывать большое количество потоков (~ 20000)?

Мы обнаружили это .Встроенный пул потоков NET был наихудшим подходом, поскольку проект представлял собой веб-приложение.Причина:веб-приложение использует встроенный пул потоков (который является статическим и, следовательно, общим для всех применений в рамках запущенного процесса) для выполнения каждого запроса в отдельном потоке, обеспечивая при этом эффективное повторное использование потоков.Использование одного и того же пула потоков для нашей внутренней обработки должно было исчерпать его и не оставить свободных потоков для пользовательских запросов или испортить их производительность, что было неприемлемо.

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

Должен ли я использовать решение NoSQL вместо MySQL?

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

  • Разработайте свой код таким образом, каждое задание будет влиять только на свое собственное подмножество строк в базе данных (сюда входят и другие таблицы).Если вы сможете это сделать, вам не понадобятся какие-либо явные блокировки на уровне базы данных (в виде уровней сериализации транзакций).Вы даже можете применить либеральный уровень сериализации, который может допускать грязное или фантомное чтение - это будет выполняться быстрее.Но остерегайтесь, вы должны тщательно следить за тем, чтобы ни одно задание не совпадало по одним и тем же строкам.Этого трудно достичь в реальных проектах, поэтому вам, вероятно, следует поискать альтернативные подходы к блокировке БД.

  • Используйте соответствующий режим сериализации транзакций. Режим сериализации транзакций определяет поведение блокировки на уровне базы данных.Вы можете настроить его так, чтобы блокировать всю таблицу, только те строки, на которые вы влияете, или вообще ничего.Используйте его с умом, поскольку любое неправильное использование может повлиять на согласованность данных, целостность и стабильность всего приложения или сервера БД.

  • Я не знаком с базой данных NoSQL, поэтому могу только посоветовать вам изучить возможности параллелизма и сопоставить их с вашим сценарием.В итоге вы могли бы получить действительно подходящее решение, но вы должны проверить в соответствии с вашими потребностями.Судя по вашему описанию, вам придется поддерживать одновременные операции с данными над объектами одного и того же типа (что является аналогом таблицы).

Является ли это лучшим решением для решения такого варианта использования?

И да, и Нет.

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

  • НЕТ, потому что если вы работаете над реальным проектом, вам нужно что-то надежное.Если у вас так много вопросов, вам, очевидно, потребуется время, чтобы созреть и выработать стабильное решение для такой задачи.Многопоточность - сложная тема по многим причинам:

    • Это трудно отлаживать
    • Это вводит множество точек отказа, вы должны быть осведомлены обо всех из них
    • Другим разработчикам может быть сложно помогать вам или работать с вашим кодом, если только вы не придерживаетесь общепринятых правил.
    • Обработка ошибок может быть сложной задачей
    • Поведение непредсказуемо / недетерминировано.

    Существуют решения с высоким уровнем зрелости и надежности, которые являются предпочтительным подходом для реальных проектов.Недостатком является то, что вам придется изучить их и изучить, насколько они адаптируются к вашим потребностям.

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


И последний, но не в последнюю очередь, Я не видел никаких прогнозов обработки ошибок с вашей стороны.Подумайте и изучите, что делать, если задание провалится.По крайней мере, добавьте статус "СБОЙ" или что-то еще, что сохранится в таком случае.Обработка ошибок сложна, когда дело доходит до потоков, поэтому тщательно проводите свои исследования и применяйте методы.

Удачи вам

Другие советы

Вы можете объявить максимальный размер пула с помощью ThreadPoolExecutor#setMaximumPoolSize(int).Как Integer.MAX больше 20000, тогда технически да, это возможно.

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

Ты не должен быть проблемой для адрес ~ 20 000 потоков на современном настольном компьютере или ноутбуке, но на мобильном устройстве это может быть проблемой.

Из документа:

Основные и максимальные размеры пула

ThreadPoolExecutor автоматически отрегулируйте размер пула (см. getPoolSize()) в соответствии с установленными ограничениями с помощью corePoolSize (см. getCorePoolSize()) и maximumPoolSize (см. getMaximumPoolSize()).При отправке новой задачи в методе выполнить(java.lang.Работоспособный), и количество потоков меньше, чем в corePoolSize выполняется, создается новый поток для обработки запроса, даже если другие рабочие потоки простаивают.Если запущено больше потоков corePoolSize, но меньше , чем maximumPoolSize, новый поток будет создан только если очередь заполнена.Установив одинаковые значения corePoolSize и maximumPoolSize , вы создаете пул потоков фиксированного размера.Задавая maximumPoolSize практически неограниченное значение, такое как Целое число.MAX_VALUE, вы позволяете пулу выполнять произвольное количество одновременных задач.Как правило, основной и максимальный пул размеры устанавливаются только при построении, но они также могут быть изменены динамически с использованием setCorePoolSize(int) и setMaximumPoolSize(int).

Еще

О БД.Создайте решение, которое не зависит от структуры базы данных.Затем вы можете настроить две среды и измерить их.Начните с технологии, которую вы знаете.Но будьте открыты для других решений.С самого начала база данных отношений должна соответствовать производительности.И если вы будете правильно ухаживать за чесоткой, то позже это не должно стать проблемой.NoSQL используется для работы с действительно большими данными.Но лучше всего для вас создать и то, и другое и запустить несколько тестов производительности.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top