Распределенные системы. Паттерны проектирования - страница 29
Сервисы с репликацией на уровне приложения
Во всех предыдущих примерах репликация и распределение нагрузки происходят на сетевом уровне сервиса. Распределение нагрузки не зависит от конкретного протокола взаимодействия узлов, находящегося в стеке над TCP/IP. Однако многие прило-жения общаются по протоколу HTTP, и, зная протокол общения узлов, можно расширить паттерн реплицированного stateless-сервиса дополнительной функциональностью. Добавляем кэширующую прослойку Иногда код stateless-сервиса достаточно сложен в вычислитель-ном плане, несмотря на то что состояние сервиса не хранится. Для обслуживания запросов может потребоваться обращение к базе данных, выполнение сложной обработки или визуализации данных. В таких условиях отнюдь не помешает добавить в при-ложение прослойку для кэширования. Кэш находится между 90 Часть II. Паттерны проектирования обслуживающих систем stateless-приложением и запросом конечного пользователя. Про-стейшая форма кэширования в веб-приложениях — применение кэширующего веб-прокси. Кэширующий прокси представляет собой просто-напросто HTTP-сервер, хранящий в памяти состоя-ние запросов пользователей. Если два пользователя запросят одну и ту же веб-страницу, только один из запросов будет адресован приложению, второй будет обслужен из памяти кэша (рис. 5.4).
Рис. 5.4. Работа кэширующего сервера
Для наших целей воспользуемся кэширующим веб-сервером Varnish с открытым исходным кодом ( https://varnish-cache.org/ ). Развертывание кэширующего сервера Простейший способ развертывания веб-кэша — рядом с каж-дым экземпляром сервиса при использовании паттерна Sidecar (рис. 5.5).
Такой подход при всей простоте имеет недостатки. В частности, вам придется масштабировать кэш одновременно с приложени-ем. Это не всегда желательно. Для кэша следует использовать наименьшее количество экземпляров с наибольшим количеством памяти (например, не десять копий с 1 Гбайт памяти у каждой, а две копии с 5 Гбайт памяти у каждой). Для того чтобы понять, почему так лучше, представьте, что каждая страница кэширует-ся в каждом экземпляре. При десяти экземплярах кэша каждая Глава 5. Реплицированные сервисы с распределением нагрузки 91
Рис. 5.5. Добавление кэширующего веб-сервера
в виде контейнера-прицепа
страница будет записана десять раз, что уменьшит общее коли-чество разных страниц, одновременно находящихся в кэше. Это снижает коэффициент попадания — долю запросов, обслужива-емых из кэша, что, в свою очередь, уменьшает полезность кэша. И хотя желательно иметь как можно меньше крупных экземпля-ров кэш-серверов, небольших экземпляров веб-серверов должно быть как можно больше. Многие языки, например NodeJS, могут задействовать только одно процессорное ядро, и поэтому имеет смысл создавать много экземпляров сервиса, чтобы в полной мере использовать преимущества многоядерных систем, даже в рамках одной машины. Следовательно, имеет смысл настроить кэширу-ющую прослойку как другой реплицированный stateless-сервис, находящийся над веб-сервисом (рис. 5.6).
92 Часть II. Паттерны проектирования обслуживающих систем
Рис. 5.6. Добавление кэширующей прослойки к реплицированному сервису Практикум. Развертывание кэширующей прослойки
Сервис dictionary-server, который мы развернули ранее, распре-деляет трафик по экземплярам сервера-словаря и может быть найден по DNS-имени dictionary-server-service . Данный паттерн изображен на рис. 5.7.
Начнем создание кэширующей прослойки с настройки кэши-рующего сервера Varnish:
vcl 4.0;
backend default {
.host = "dictionary-server-service";
.port = "8080";
Глава 5. Реплицированные сервисы с распределением нагрузки 93
Рис. 5.7. Добавление кэширующей прослойки