Масштабируемость RESTful web-сервисов

Representational State Transfer это не только эффективный стиль для архитектуры данных и приложений. Безусловно, очень удобно, когда у каждого информационного ресурса есть неизменный URL.  Такую гиперссылку можно сохранить или переслать коллеге, избавив информационные системы от повторного поиска. Но придумывался REST, как впрочем и HTTP, не только для этого. Основная задача этой архитектуры заключалась в построении масштабируемых многоуровневых приложений. Не смотря, на очевидность и простоту этой архитектуры, очень многие разработчики корпоративных информационных систем её не понимают (или делают вид, что не понимают). Поэтому, очередной взгляд на эту архитектуру. На этот раз с точки зрения инфраструктуры.

Протокол HTTP предоставляет нам набор методов для работы с информационными ресурсами. Некоторые методы, например метод PUT, являются идемпотентными. Т.е. если мы не уверены, отработал ли наш вызов этого метода, мы можем вызвать его повторно. Результат многократных вызовов идемпотентного метода не приведет к разным состояниям ресурса. Методы GET и HEAD, кроме того, являются безопасными. Т.е. их использование никак не повлияет на исходный ресурс.

Коснемся этих методов чуть подробнее. Метод GET предназначен для чтения ресурса. Метод HEAD это то же самое, только непосредственно сам ресурс не загружается, а загружается только его заголовок (метаданные). Выделение в протоколе методов чтения преследует вполне понятную цель. Такие данные можно кэшировать. Между клиентом и сервером вы можете поставить произвольно количество прокси-серверов, на которых хранить копии ресурсов. Значительная часть клиентских запросов обработается на уровне прокси и не будет создавать на нагрузку на сервер исходных данных. Кроме того, у метода GET есть дополнительные заголовки If-Modified-Since, If-Range и т.д., позволяющие не загружать ресурс, если он не изменился с указанного в запросе времени или загружать только часть ресурса. В общем, все сделано для того, чтоб строить достаточно эффективные архитектуры. Просто пока стоимость жестких дисков снижается быстрее, чем зарплата ИТ-архитектора в этом нет экономической необходимости.

Следующим фундаментальным свойством RESTful сервиса является то, что он не поддерживает сессию (stateless). Все данные, обрабатываемые в одном запросе-ответе должны быть самодостаточны. Взаимодействие не предполагает наличие какой-либо предыстории. Т.е. нет никаких курсоров, текущих элементов, заданных ранее в ходе взаимодействия значений и т.д. В принципе, это верно и просто для HTTР но начинающий веб-программист, обязательно, возразит мне, что в любом инструменте для разработки веб-приложений есть переменная «сессия». В этой переменной удобно хранить логин аутентифицировавшегося пользователя, коннект с базой данных, введенные пользователем ранее параметры и т.д. С точки зрения программиста выглядит это хорошо. С точки зрения балансировки нагрузки – не очень. RESTful явно запрещает такие сессии, избавляя нас от проблем масштабируемости. Вы можете поставить произвольно количество одинаковых серверов и «разбрасывать» по ним вызовы произвольным образом. Вы можете разделить хранилище данных на любое количество частей и легко построить маршрутизацию запросов к нужному серверу. Причем речь идет не только о запросах на чтение, но и на модификацию и добавление данных. Записи с четными номерами положить в одну базу данных, с нечетными – в другую, а с номерами, кратными 42 разместить в облачном датацентре на Луне (или на барже в Индийском океане, говорят это сейчас модно). И вам не придется синхронизировать состояния этих серверов. Таких состояний просто нет. У ресурсов состояния есть, а у операций – нет.

Здесь надо вернуться на прикладной уровень и вспомнить Hypermedia as the Engine of Application State. Эта идея еще и о том, что связи между ресурсами хранятся внутри самих этих ресурсов, в виде гиперссылок. Т.е. нет каких-то отдельных таблиц отношений, показывающих, что «сотрудник А работает в отделе B», все внутри ресурса. И  изменение связей между ресурсами производится операцией изменения ресурса (POST). Вы, конечно, можете создать отдельную связь между двумя ресурсами, но это будет новый ресурс и включать он будет исключительно ссылки на объединяемые ресурсы. Исходные ресурсы эта операция не затрагивает. Им все равно, в какое количество ассоциаций, иерархий или категорий их включили. По сути, это означает, что структуры данных потеряли границы. Первый элемент связного списка лежит на одном сервере, следующий на другом, а третий плывет на барже в Индийском океане. Глобальное адресное пространство. Главное, избавиться от старой программисткой привычки десятками создавать копии данных, тогда и синхронизировать их не придется.