Распределенное Java приложение на Docker Рой с многопоточностью - PullRequest
0 голосов
/ 29 января 2020

Я выполняю миграцию веб-приложения Grails (2.4.4) (на основе Java) в среде Docker Swarm (3 узла) с MariaDB база данных снаружи Рой.

В своей текущей версии приложение размещается на одном сервере и создает несколько потоков (~ 10), которые все получили задание c, но все они манипулируют данными из базы данных.

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

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

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

Итак, будем рады услышать любой совет по этому вопросу! Спасибо

Ответы [ 3 ]

2 голосов
/ 09 февраля 2020

Здесь есть много возможных дизайнов, все они в значительной степени зависят от того, чего вы на самом деле пытаетесь достичь:

Вы говорите:

В текущей версии приложения размещается на одном сервере и создает несколько потоков (~ 10), для которых у всех есть заданная задача c

Предположим, вы go с опцией 2, и все потоки работают на одном узле из трех.

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

Но если это так, рабочий узел будет «поглощать» всю нагрузку до тех пор, пока не выйдет из строя, тогда вся нагрузка попадет на узел, который становится активным, но, возможно, его слишком большая нагрузка и он потерпит неудачу, тогда третий узел выйдет из строя, как домино:)

Еще одна проблема с этим подходом заключается в том, как на самом деле сделать node2 активным, когда node1 не удалось, Кто решит, что node2 (в отличие от node3) сейчас активен? Как вы создаете потоки, если конфигурация "run-Without-Threads" уже указана?

Если вы можете ответить на эти вопросы, не ожидайте высокого давления на один узел и согласитесь поддерживать узлы для резервирования - может ли это быть способом go, многие системы построены таким образом, поэтому Это жизнеспособное решение.

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

Здесь есть много возможных вариантов. Вы говорите

, для которого все получили заданную задачу c

Кто на самом деле запускает эту задачу для выполнения? Это какое-то запланированное задание, которое запускается время от времени и передает задание на выполнение? Или, может быть, задача порождается в результате некоторого запроса от клиента (например, через HTTP-вызов)?

Другой вопрос: существуют ли задачи, которые в принципе не должны перекрываться, или потенциально каждая задача может повредить выполнение другая задача?

Если есть возможность разделить задачи, вы можете отправить сообщение в некоторую очередь, когда прибудет новая задача, и на основе какого-либо partitionId (если вы используете что-то вроде kafka) или ключа маршрутизации в rabbit mq или любом другом В качестве способа кластеризации можно создать архитектуру, в которой задачи можно сгруппировать по типу, а один указанный c сервер будет заботиться о выполнении всей группы задач, другая группа задач может быть выполнена другим сервером.

Если сервер будет go выключен, то группа задач, ранее обработанных отказавшим сервером, будет переназначена на другой сервер (технические характеристики зависят от решения).

0 голосов
/ 03 февраля 2020

Вы либо изменяете logi c, чтобы приложение запускало меньше потоков на экземпляр, а затем вместо одного экземпляра с 10 потоками запускаете 5 экземпляров с 2 потоками в каждом и просите Swarm масштабировать его для вас.

Другой вариант - соединить несколько экземпляров в один «кластер» и использовать некоторый механизм для выбора лидера и запуска всех протекторов только на узле лидера. И затем, если лидер выходит из строя, вам нужно переизбрать лидера и перезапустить задачи для нового лидера.

0 голосов
/ 29 января 2020

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

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

Другой вариант заключается в выделении кода потока в приложение «рабочего стиля».

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

Рабочее приложение не обязательно должно быть основано на Grails, и вы можете выбрать любой другой фреймворк с собственной поддержкой Groovy, например Vert.x (рекомендуется), Micronaut или Ratpack.

...