Архитектуру REST часто путают с некой методикой именования URL на сайте, правилами применения HTTP-методов: GET, POST, PUT, DELETE. Но REST — мощнейший архитектурный стиль, затрагивающий всё приложение, концентрирующийся на производительности и совместимости с другими системами.

Между веб-сервером, на котором работает сайт, и браузером клиента находятся многочисленные промежуточные узлы, обрабатывающие и кэширующие передаваемую информацию. На сайт заходят поисковые роботы. Всем им безразличны архитектурные велосипеды — они воспринимают веб-сайт, как черный ящик с HTTP-интерфейсом. Если веб-сервер точно следует требованиям протокола, он работает эффективно.

Унифицированный программный интерфейс

Однаждына поддержку пришел проект с большим каталогом товаров. У каждого товара — своя страничка с фотографиями, описанием, отзывами и т.д. Всё как обычно. Но клиент жаловался на то, что на почту в большом объеме льются письма с информацией о выставлении товарам оценок. На что я обратил внимание: звездочки в голосовалке товаров были оформлены ссылками на php-скрипт. Скрипт запоминал оценку и высылал уведомление клиенту на почту. Откуда же столько оценок? Поисковый робот регулярно во время индексации сайта заходил на страничку товара, видел набор ссылок и начинал переходить по этим ссылкам, нечаянно запуская скрипт выставления оценки. К слову, он заходил по всем ссылкам равномерно, поэтому оценки получались средние.

Почему это происходило? Поисковый робот воспринимал GET-запросы, которые он делает, как безопасные и идемпотентные. Но обработчик был небезопасным и неидемпотентным.

МетодБезопасныйИдемпотентный
GETДаДа
HEADДаДа
OPTIONSДаДа
PUTНетДа
DELETEНетДа
POSTНетНет

Безопасный запрос — это запрос, который не меняет состояние приложения.

Идемпотентный запрос — это запрос, эффект которого от многократного выполнения равен эффекту от однократного выполнения.

Так как POST-запросы небезопасные и неидемпотентные, браузер никогда не повторяет отправку формы POST-запросом автоматически.

Идемпотентный запрос можно повторять многократно, если нет уверенности, что предыдущий такой же запрос был выполнен. Рассмотрим пример с коллекцией книг в библиотеке. Если необходимо удалить книгу из библиотеки по определенному ISBN, используя для этого DELETE-запрос, можно будет послать повторный запрос на удаление. Результатом даже нескольких запросов будет удаление книги по данному ISBN — ожидаемый результат. Если необходимо поменять название у определенной книги PUT запросом, то без опасения можно послать несколько запросов. Результатом будет изменение названия книги на необходимое.

В то же время многократное обращение к функции добавления книги посредством POST-запроса приведет к добавлению нескольких книг. Именно поэтому повторные неидемпотентные запросы могут привести к неожиданным результатам.

Из принципов идемпотентности и безопасности следуют отношения между HTTP-методами и CRUD-операциями.

HTTP-методОперация
POSTCreate
GETRead
PUTUpdate
DELETEDelete

Унификация интерфейса позволяет не делать типовых ошибок и не нарушать семантику HTTP-запросов.

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

Не храним состояние клиента

Еще один веб-сайт, реализованный на коммерческом движке интернет-магазина, хранил сессии в БД. Для генерирования каждой страницы он запускал сессии. В момент старта сессии он создавал запись в таблице БД, а когда заканчивал обрабатывать запрос клиента, сохранял сессионные данные. Сессии идентифицировал по кукам. Если приходил поисковый робот (а он обычно не запоминает куки), то каждая сгенерированная страничка порождала старт сессии и вставку в сессионную таблицу. Вместе с приличной посещаемостью веб-сайта это приводило к большой нагрузке на сессионную таблицу: постоянно шли запросы на создание сессии, на её обновление. А вставка больших сериализованных данных из сессии в БД занимала приличное количество времени и иногда подвешивала сайт.

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

Сессии при неграмотном применении — зло. Они мешают производительности и масштабируемости проекта. Если их не получается избегать, то необходимо локализовывать их применение.

Кэширование

PHP-разработчики любят memcache. Когда возникают проблемы с производительностью, то предлагают закэшировать или тяжелые запросы к БД, или часть генерируемой страницы, или даже вообще весь контент страницы, если сессионные данные не мешаются. И это называется оптимизацией производительности.

Печальная правда состоит в том, что большинство проектов, которые начинают прикручивать memcache, могли бы этого не делать при их посещаемости, если бы следовали REST-архитектуре.

Протокол HTTP содержит все необходимые заголовки для организации кэширования как на стороне клиента, так и промежуточными звеньями между клиентом и веб-сервером. Эти заголовки позволят избегать лишних запросов к серверу, а также при необходимости размещать непосредственно перед веб-сервером специализированное ПО для кэширования (например, Squid), внося минимум дополнительной логики в само приложение.

Обычно кэширующие заголовки выставляют только для успешных запросов (200 OK), но также можно кэшировать и другие запросы, в том числе с ошибками:

Правильно разбивая веб-приложение на компоненты, организуя прозрачное кэширование на стороне клиента, можно добиться значительных результатов. Но прежде всего необходимо представлять, как работает протокол HTTP, чтобы использовать все его возможности и глубже понимать принципы построения RESTful-приложений.