Для кэширования HTTP-трафика в приложении активно использовалось средство Squid, и мы заметили появившуюся проблему довольно быстро, поскольку видели, что средство Squid стало обходить больше запросов, направляющихся к серверам приложения. Мы внесли исправление в код кэш-заголовков и выдали новый выпуск, а также вручную вычистили соответствующую область Squid-кэша. Но этого оказалось недостаточно.
Как уже упоминалось, кэширование может проводиться в нескольких местах. Когда дело касается обслуживания контента для пользователей общедоступного веб-приложения, между вами и клиентом может быть несколько средств кэширования. Перед сайтом может быть не только что-нибудь вроде CDN-сети — кэширование может использоваться также некоторыми поставщиками интернет-услуг. Можете ли вы контролировать такие средства кэширования? И даже если можете, остается еще одно средство кэширования, над которым у вас нет практически никакого контроля, — это кэш в браузере пользователя.
Страницы со свойством постоянной свежести Expires: Never застревают в средствах кэширования многих ваших пользователей и никогда не будут аннулированы, пока кэш не будет заполнен или пользователь не очистит его вручную. Понятно, что мы не можем инициировать ни то, ни другое событие; единственное, что можно сделать, — изменить URL-адреса таких страниц, чтобы инициировать их повторное извлечение.
Конечно же, кэширование может быть весьма эффективным, но при этом нужно представлять себе весь путь подвергаемых кэшированию данных от источника до пункта назначения, чтобы по-настоящему оценить все сложности и все места, где что-то может пойти не так.
Если вам посчастливилось получить полностью автоматизированное выделение виртуальных хостов и вы можете полностью автоматизировать развертывание экземпляров микросервисов, значит, у вас есть строительные блоки, позволяющие автоматически масштабировать микросервисы.
Например, у вас также может применяться масштабирование, запускаемое при известных тенденциях. Вам может быть известно, что пиковая нагрузка на систему наблюдается между 9:00 и 17:00, поэтому вы подключаете дополнительные экземпляры в 8:45 и выключаете их в 17:15. Если вы пользуетесь такими средствами, как AWS, в котором имеется очень хорошая встроенная поддержка автоматического масштабирования, выключение ненужных экземпляров поможет сэкономить деньги. Чтобы узнать, как изменяется нагрузка в течение дня и недели, понадобятся соответствующие данные. У некоторых разновидностей бизнеса имеются также вполне очевидные сезонные циклы, поэтому, для того чтобы сделать правильные выводы относительно объема вызовов, вам нужны ретроспективные данные.
В то же время можно быстро реагировать, подключая дополнительные экземпляры при обнаружении повышения нагрузки или сбоя экземпляра и удаляя экземпляры, когда надобность в них отпадет. Основным фактором здесь является знание того, насколько быстро вы сможете выполнить масштабирование, заметив восходящую тенденцию. Если вы знаете, что для обнаружения тенденции понадобится всего лишь пара минут, а масштабирование может занять минимум десять минут, значит, для преодоления этого разрыва нужно обзавестись дополнительными мощностями. В таком случае возникает необходимость в применении хорошего набора нагрузочных тестов. Ими можно воспользоваться для тестирования правил автоматического масштабирования. Если тесты, способные воспроизвести различные нагрузки для запуска масштабирования, отсутствуют, то действенность выбранных правил останется проверять только в производственном режиме. И негативные последствия неправильного выбора не будут слишком велики!
Хорошим примером бизнеса, для которого может потребоваться сочетание масштабирования на основе прогноза и на основе реагирования на изменение нагрузки, может послужить сайт. Наблюдая за последним новостным сайтом, над которым мне пришлось работать, мы заметили четкие дневные тенденции с ростом нагрузки с утра до обеда и последующим ее спадом. Эта схема повторялась изо дня в день, а в выходные дни трафик был менее выраженным. Это позволяет выявить весьма четкую тенденцию, которая может управлять упреждающим масштабированием ресурсов путем их добавления (и высвобождения). В то же время какое-нибудь интересное событие может вызвать неожиданный всплеск нагрузки, требующий кратковременного более объемного выделения ресурсов.
Я считаю, что автоматическое масштабирование используется главным образом для того, чтобы справиться со сбоями экземпляров, а не для того, чтобы оперативно реагировать на условия нагрузки. В AWS вы можете определять правила вроде «в этой группе должно быть не менее пяти экземпляров», чтобы при сбое одного из них автоматически запускался новый экземпляр. Я видел, как такой подход приводил к веселой игре в «ладушки», когда кто-то забывал выключить правило, а затем пытался уменьшить количество экземпляров с целью проведения обслуживания и наблюдал за тем, как они все продолжали запускаться!
Полезны оба вида масштабирования, как на основе прогноза, так и на основе реагирования на изменение нагрузки, и оба они могут существенно повысить экономическую эффективность при использовании платформы, позволяющей платить только за использованные вычислительные ресурсы. Но они также требуют тщательного наблюдения за доступными вам данными. Я бы посоветовал использовать автоматическое масштабирование в первую очередь при возникновении сбоев в ходе сбора данных. Если потребуется приступать к масштабированию при изменении нагрузки, нужно проявить особую осторожность и не допустить слишком быстрого возврата мощностей. В большинстве случаев лучше иметь больше вычислительных мощностей, чем нужно, вместо того чтобы испытывать их острый дефицит!
Хотелось бы получить абсолютно все, но, к сожалению, мы знаем, что это невозможно. А что касается распределенных систем, подобных тем, что создаются с использованием архитектур микросервисов, есть даже математические доказательства того, что это невозможно. Может, вы уже слышали про теорему CAP, особенно при обсуждении достоинств различных типов хранилищ данных. Ее суть заключается в том, что в распределенных системах есть три компромиссных по отношению друг к другу свойства: согласованность, доступность и терпимость к разделению[5]. В частности, в теореме говорится, что при отказе удается сохранить только два из них.
Согласованность является характеристикой системы, означающей, что при обращении к нескольким узлам будет получен один и тот же ответ. Доступность означает, что на каждый запрос будет получен ответ. Терпимость к разделению является способностью системы справляться с тем фактом, что установить связь между ее частями порой становится невозможно.
После того как Эрик Брюер (Eric Brewer) опубликовал исходную гипотезу, идея получила математическое доказательство. Я не собираюсь в него углубляться не только потому, что это не относится к теме книги, но и потому, что не могу гарантировать, что сделаю это правильно. Воспользуемся лучше несколькими практическими примерами, которые помогут разобраться в том, что за всем этим стоит, и в том, что теорема CAP является выжимкой из весьма логичного набора рассуждений.
Мы уже говорили о некоторых простых технологиях масштабирования баз данных. Возьмем одну из них, чтобы исследовать идеи, положенные в основу теоремы CAP. Представим, что сервис инвентаризации развернут на базе двух отдельных дата-центров (рис. 11.8). В каждом дата-центре экземпляры сервиса поддерживает база данных, и эти две базы данных обмениваются данными, стараясь синхронизировать их между собой. Операции чтения и записи осуществляются через локальный узел базы данных, а для синхронизации данных между узлами применяется репликация.
Теперь подумаем о том, что случится, когда что-нибудь откажет. Представим, что прекратило работу что-либо настолько простое, как сетевая связь между двумя дата-центрами. С этого момента синхронизация даст сбой. Записи, осуществляемые в основную базу данных в дата-центре DC1, не будут дублироваться на дата-центр DC2, и наоборот. Большинство баз данных, поддерживающих такие настройки, поддерживают также какую-либо разновидность технологии выстраивания очередей, чтобы впоследствии обеспечить возможность восстановления из этой ситуации. Но что произойдет до такого восстановления?
Рис. 11.8. Использование репликации двух основных баз для распределения данных между двумя узлами баз данных
Предположим, что сервис инвентаризации нами полностью не отключен. Теперь при внесении изменений в данные в DC1 база данных в DC2 их не видит. Это означает, что любой запрос, обращенный к узлу инвентаризации в DC2, увидит потенциально устаревшие данные. Иными словами, система все еще доступна на обоих узлах, способных обслуживать запросы, и мы сохранили работоспособность системы, несмотря на разделение, но утратили согласованность. Зачастую это называется AP-системой. Мы не можем сохранить все три свойства.
Если в период данного разделения будет продолжено получение записей, значит, мы смирились с тем фактом, что через некоторое время записи будут рассинхронизированы. Чем дольше продлится разделение, тем труднее будет восстанавливать синхронизацию.
Реальность такова, что даже при отсутствии сетевого сбоя между узлами баз данных репликация данных не проводится мгновенно. Как уже говорилось, системы, которые готовы поступиться согласованностью для сохранения терпимости к разделению и доступности, называются согласованными по прошествии некоторого времени. Иначе говоря, ожидается, что в некоторый момент в будущем обновленные данные станут видны всем узлам, но это не произойдет немедленно, поэтому придется смириться с вероятностью того, что пользователи увидят устаревшие данные.