Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Минимизация координации для достижения масштабируемости
Большинство облачных приложений состоят из нескольких служб приложений, таких как веб-интерфейсы, базы данных, бизнес-процессы и отчеты и анализ. Чтобы обеспечить масштабируемость и надежность, каждая из этих служб должна выполняться на нескольких экземплярах.
Несогласованные системы, где работа может обрабатываться независимо без необходимости передавать сообщения между компьютерами, обычно проще масштабировать. Координация обычно не является двоичным состоянием, а спектром. Координация выполняется на разных уровнях, таких как данные или вычисления.
Что произойдет, если два экземпляра попытаются одновременно выполнить операции, влияющие на общее состояние? В некоторых случаях требуется координация действий между узлами, например для поддержки гарантий ACID. На этой схеме служба Node2 ожидает, пока Node1 снимет блокировку базы данных:
Любая координация связана с ограничением возможностей, предоставляемых горизонтальным масштабированием, и определенными сложностями. В этом примере конфликты блокировки будут возникать все чаще по мере горизонтального увеличения масштаба приложения и числа экземпляров. В самом худшем случае фронтенд-экземпляры будут большую часть времени ожидать блокировок.
Еще одна распространенная причина, требующая координации, — семантика запроса "только один раз". Например, каждый заказ должен быть обработан только один раз. Два работника ожидают новых заказов.
Worker1 принимает заказ для обработки. Теперь приложение должно гарантировать следующее: 1) Worker2 не будет дублировать эту работу, 2) заказ не будет потерян в случае сбоя Worker1.
Вы можете применить для координации рабочих потоков шаблон Планировщик, агент, контролер Но в нашем примере удобнее будет секционировать работу. Каждой рабочей роли назначается некоторое число заказов (например, с привязкой к региону выставления счетов). Если рабочий процесс завершится сбоем, его новый экземпляр начнет работу там, где остановился предыдущий, но соседние экземпляры не будут конкурировать.
Рекомендации
Используйте отдельные компоненты, которые асинхронно взаимодействуют. Компоненты должны в идеале использовать события для взаимодействия друг с другом.
Примите конечную согласованность. Если данные распределяются между несколькими объектами, только координация между ними обеспечит строгую согласованность. Предположим, что определенная операция обновляет две базы данных. Вместо того, чтобы включать эту операцию в одну транзакцию, создайте в системе механизм обеспечения итоговой согласованности. Например, шаблон компенсирующих транзакций позволит выполнить логический откат в случае сбоя.
Используйте события предметной области для синхронизации состояний. Событие предметной области регистрируется, когда в пределах некоторой предметной области происходит что-то важное. Такие события могут прослушиваться всеми заинтересованными службами, что позволяет обойтись без глобальных транзакций для координации действий между несколькими службами. Чтобы применить такой подход, система должна поддерживать итоговую согласованность (см. предыдущий пункт).
Изучите такие шаблоны, как CQRS и шаблон источников событий. Эти два шаблона минимизируют вероятность конфликтов между рабочими нагрузками чтения и записи.
Шаблон CQRS разделяет операции чтения и записи. В некоторых реализациях данные для чтения физически отделены от данных для записи.
В шаблоне источников событий все изменения состояния сохраняются в виде последовательности событий в хранилище постоянного пополнения. Добавление одного события в такой поток является атомарной операцией и требует минимальных блокировок.
Эти два шаблона дополняют друг друга. Если в хранилище, которое в соответствии с шаблоном CQRS используется только для записи, применяется событийное моделирование, хранилище только для чтения может ожидать те же события, чтобы создать читаемый моментальный снимок текущего состояния, оптимизированный для обработки запросов. Но прежде, чем внедрять CQRS или событийное моделирование, осознайте для себя сложности такого подхода.
Секционирование данных и состояния. Не включайте все данные в одну схему данных, которую используют сразу несколько служб приложения. Архитектура микрослужб принудительно применяет этот принцип, назначая каждой службе собственное хранилище данных. В одной базе данных секционирование данных на сегменты может повысить параллелизм, так как служба записи в один сегмент не влияет на запись службы в другой сегмент. Несмотря на то, что секционирование добавляет некоторую степень координации, можно использовать секционирование для повышения параллелизма для повышения масштабируемости. Разделите монолитную структуру на более мелкие части, чтобы данные могли управляться независимо.
Применяйте идемпотентные операции. Если возможно, проектируйте операции как идемпотентные. Это позволит обрабатывать их с использованием семантики "по крайней мере один раз". Например, вы можете поместить рабочие элементы в очередь. Если рабочий процесс завершается сбоем в середине операции, другой работник берет на себя рабочий элемент. Если работнику необходимо обновить данные и отправить другие сообщения в рамках своей логики, следует использовать шаблон обработки идемпотентных сообщений.
Используйте оптимистичный контроль конкурентного доступа, если это возможно. Пессимистическое управление параллелизмом подразумевает блокировку базы данных для предотвращения конфликтов. Это может снижать производительность и доступность системы. Если применить оптимистическое управление параллелизмом, каждая транзакция изменит собственную копию (моментальный снимок) данных. Когда транзакция будет зафиксирована, ядро СУБД проверит эту транзакцию и отклонит ее, если она может повлиять на согласованность базы данных.
База данных SQL Azure и SQL Server поддерживают оптимистический параллелизм, выполняя изоляцию моментальных снимков. Некоторые службы хранилища Azure, включая Azure Cosmos DB и Azure Storage, поддерживают оптимистическую блокировку с помощью тегов ETag.
Применяйте MapReduce или другой алгоритм распределенной параллельной обработки. В зависимости от данных и типа выполняемых работ можно разделить работу на независимые задачи, которые могут выполняться несколькими узлами параллельно. См. статью Стиль архитектуры для больших вычислений.
Используйте выбор лидера для координации. В тех случаях, когда операции необходимо координировать, сделайте так, чтобы координатор не был единой точкой отказа в приложении. Если реализовать шаблон выборов лидера, в каждый момент времени один из экземпляров будет лидером, который выполняет функции координатора. Если лидер падает, выбирается новый экземпляр, который станет лидером.