Одна из основных предпосылок веб-служб RESTful заключается в том, что HTTP следует рассматривать как протокол приложения, а не только как транспортный протокол. Он включает в себя целый набор семантики, который позволяет нам создавать надежные распределенные системы. А в некоторых случаях, когда несколько потребителей манипулируют одним и тем же ресурсом, изменяя его состояние, решение должно быть достаточно надежным, чтобы предотвратить попадание системы в состояние гонки (race condition).
Как HTTP может предотвратить это?
HTTP обеспечивает простой , но в тоже время сильный механизм выравнивания состояния ресурсов путем использования ETag и условных заголовков запроса . ETag – это все, что однозначно идентифицирует объект, например идентификатор, связанный с постоянным ресурсом, контрольную сумму заголовков и тела объекта и т. д. Если этот ресурс изменяется, то есть когда один или несколько его заголовков или объект body, изменяется – тег объекта изменяется соответствующим образом, отражая это новое состояние ресурса.
Когда ответ содержит ETag, связанный с состоянием ресурса, и вы хотите продолжить работу с этим же ресурсом, рекомендуется использовать этот тег в последующих запросах (называемых условными запросами), в противном случае состояние ресурса может в конечном итоге перестать синхронизироваться с сервисом. Возвращая что-то вроде 409 Conflict
.
Условные запросы происходят, когда текущий ETag передается в заголовок условного запроса, например If-Match
или If-None-Match
, когда пользователь запрашивает обновление ресурса. Затем служба проверит предварительное условие, сравнив текущий ETag ресурса с тем, который указан в запросе. Если он удовлетворен, сервер переходит к обработке запроса, в противном случае он приходит к выводу, что ресурс изменился, и отвечает, отправляя 412 Precondition Failed
.
Пример
Представлен интернет-магазин товаров для дома, где два сотрудника – Admin1 и Admin2 – отвечают за управление содержимым. В этом сценарии оба администратора пытаются изменить состояние одного и того же продукта (Weber BBQ) примерно в одно и то же время. Admin1 хочет снизить цену продукта до 300 долларов, а Admin2 хочет изменить свое состояние на «Недоступно». Сначала оба администратора получают текущее состояние продукта независимо друг от друга, выполнив следующий GET
запрос:
GET /product/1 HTTP/1.1
Host: myshop.com
Обратите внимание, что ответ сервиса содержит заголовок ETag.
HTTP/1.1 200 OK
Content-Length: 265
Content-Type: application/json
ETag: “686897696a7c876b7e”
{
“name”: “WeberFamilyBBQ”,
“description”: “Great for parties and cooks a neat roast too.”,
“price”: 399,
“status”: “InStock”
}
Затем Admin1 выполняет запрос PUT, включая If-Match
заголовок со значением ETag из предыдущего GET
.
PUT /product/1 HTTP/1.1
Host: myshop.com
If-Match: “686897696a7c876b7e”
{
“name”: “WeberFamilyBBQ”,
“description”: “Great for parties and cooks a neat roast too.”,
“price”: 399,
“status”: “InStock”
}
И поскольку состояние продукта не изменилось с момента последнего запроса, значит, запрос успешен! Обратите внимание, что ответ возвращает обновленное значение ETag, отражающее новое состояние продукта.
HTTP/1.1 204 No Content
ETag: “616898r96a8cy86b8eee11”
Через некоторое время после того, как Admin1 обновил продукт, Admin2 выполняет еще один PUT
запрос к тому же продукту, включая тот же If-Match
заголовок со значением ETag из GET
запроса.
PUT /product/1 HTTP/1.1
Host: myshop.com
If-Match: “686897696a7c876b7e”
{
“name”: “WeberFamilyBBQ”,
“description”: “Great for parties and cooks a neat roast too.”,
“price”: 399,
“status”: “InStock”
}
Затем служба определяет, что кто-то пытается изменить тот же продукт, используя устаревшее представление ресурсов (ETags разные!), И отвечает 412 Precondition Failed
. Никаких race condition!
HTTP/1.1 412 Precondition Failed
Заключение
Хотя ETags и условные заголовки запросов составляют мощный механизм для работы с параллелизмом, следует помнить о том, что в зависимости от объема вычислений, выполняемых сервером для генерации ETag, время отклика может значительно увеличиться. Так что используйте его, только если он вам нужен!
Автор Александр Мартинс