Re[5]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.02.20 06:58
Оценка:
Здравствуйте, samius, Вы писали:
S>Поставил "супер", но вот тут я принципиально не согласен. Не нужно противопоставлять RPC и REST вовсе. RPC — технология, позволяющая выполнить удаленный вызов и точка. REST — это архитектурный стиль организации работы распределенных приложений и сервисов. Это как сравнивать преимущества правил дорожного движения над автомобилем.
Под словом RPC понимают как минимум три вещи:
1. Методика, при которой у нас вызов, выглядяший в клиентском коде как обычный вызов процедуры, под капотом вызывает выполнение кода на удалённом сервисе
2. Конкретный стандарт RPC типа XML-RPC, Corba, COM
3. Стиль разработки API, в котором наружу выставляются "методы", у которых нет какой-либо семантики, кроме прикладной.

В конкретных примерах у нас зачастую все три соединяются в одном — например, у нас есть API без метаинформации о поведении методов, реализованный по стандарту XML-RPC, и к нему написана библиотека на клиентском языке, которая оборачивает вызовы эндпоинта в "процедуры".

Чисто теоретически, можно взять п.1, и приколхозить к нему инструменты для восстановления после сбоев.
Например, у всех get-методов можно добавить параметр "lastResponseTimestamp", а помимо прямого результата научить метод возвращать аналоги 304 Not Modified, а также передавать timestamp ответа. Потом можно прикрутить дельта-енкодинг.
Потом — компрессию. Потом прикрутить graceful degradation в зависимости от client capabilities, потому что у нас уже четыре версии протокола, и мы ни одну из них не можем заблокировать, т.к. под них всё ещё есть клиенты, которых мы не можем заставить проапгрейдиться.
У модифицирующих методов можно прикрутить ключи идемпотентности.

По факту, изо всего этого делают только ключи идемпотентности, да и то не всегда. Потому что технология диктует. Это как плыть против ветра — в REST это всё делается легко и естественно, и с гарантией обратной совместимости. В RPC — хренушки.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[18]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.02.20 07:02
Оценка:
Здравствуйте, Ватакуси, Вы писали:

В>И если одназночность так и не пришла, валить сервер DDOS-ом?

Откуда D DOS? Вы всё же в словаре-то смотрите незнакомые слова, прежде чем ими пользоваться
1. Со стороны сервера, вам в любом случае нужно думать о throttling. Потому что нет никакой гарантии, что клиенты ведут себя прилично, и не захотят вас просто завалить.
2. Со стороны клиента, достаточно брать паузу при ошибках. В реальной среде вы вряд ли получите DOS из-за проблем обратного канала: характерное время, за которое вы получаете таймаут — около 5 секунд (это оптимистично). Чаще делают таймауты в 30 секунд. "Долбя" сервер запросами раз в полминуты, вы никакого DOS не устроите.
3. Самое главное: а какая альтернатива? Никакого способа сделать лучше в природе не существует. См. "проблема двух генералов".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.02.20 07:12
Оценка:
Здравствуйте, Sharov, Вы писали:

S>>Так и тут — опытный архитектор сходу видит в системе то состояние, которое он будет реплицировать.

S>А причем тут опыт, если понятно, что реплицировать надо всю бд, ибо это и есть состояние?
Ну конечно же нет. Вопрос в гранулярности этого состояния. Скажем, когда я бронирую себе конференцзал, мне нафиг не нужно состояние всех конференцзалов во всех офисах компании за бесконечный период.
Всё, что мне нужно — это интересующий меня диапазон в несколько дней.
REST позволяет мне описать эту предметную область таким образом, чтобы я, с одной стороны не был вынужден реплицировать "всю БД", а с другой стороны получал гарантию того, что интересующее меня состояние up to date.
И что у меня есть детерминированный способ это состояние изменить в желаемую для меня сторону.
В RPC у нас есть искушение сделать одним из двух способов:
1. Выбор на сервере. Я посылаю длительность слота, количество человек, диапазон поиска (см. тж. сервис приобретения билетов на сайте РЖД).
Проблема: если сервер забронировал билет, а я потерял ответ, у меня нет способа восстановиться без риска получить двойное бронирование. Да, чинится, но дополнительными рукопашными исправлениями в API.
2. Выбор на клиенте. Собственно, это будет рукопашный REST, т.к. у меня будет безопасный метод "получить расписание" и идемпотентный "забронировать слот".

S>А можно подробнее зачем в этой цепочке client ->http post -->server еще и прокси? Или он тут нужен для инвалидации кэша, тогда какая ему разница какой запрос (форма запроса), главное что post/resourceid?

Прокси, естественно, нужен для улучшения производительности. Смотрите: опять концепция REST даёт нам метаинформацию о том, что происходит. К примеру, можно всех пользователей офиса А запустить через прокси, и тогда как только один из них сходит на сервер за расписанием занятости ресурсов, остальные смогут получить ответ из локального прокси, а не через океан. Репликация состояния.
S>А request id тут не поможет?
Поможет, но о нём же надо подумать. С вероятностью 95% первая версия RPC API никаких RequestID содержать не будет — он же не требуется по спецификации.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.02.20 07:21
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Станьте ёжиками! Что тут непонятного?


C>Вот пример: "PUT /user\r\n\r\n{name: "Vasiliy", last_name: "Pupkin"}". Что тут происходит при двойном запуске? Будут созданы два пользователя или один пользователь?
Конечно же один. Читаем спецификацию HTTP, понимаем, что мы создаём ресурс по адресу /user.
Ровно его же мы получим обратно, выполнив GET /user
C>Если два пользователя (ID=1,2), то в списке пользователей будем видеть лишний объект. Если же один пользователь, то тогда будет проблема, если нужно таки создать ДВУХ пользователей (Vasiliy Ivanovich Pupkin и Vasility Petrovich Pupkin).
C>И никакие мантры вида "PUT же идемпотентен" это не решают. Нужно сразу и заранее проектировать это, и там уже будет без разницы через какой глагол посылать запросы. Хоть через GET.
Не нужно. Когда вы сядете проектировать REST API для управления пользователями, то вам сразу же станет понятно, что выглядеть запрос будет так:
PUT /users/c1b01406-c3da-44d8-b8d0-61d5c7d045b6/ \r\n\r\n{name: "Vasiliy", last_name: "Pupkin"}

И отличать создание двух разных пользователей от повторных попыток создать одного и того же сервер будет легко.

Причём вы до этого дойдёте ещё до того, как начнёте делать задачу "создание пользователей". Потому что сначала вы сделаете
GET /users/

А потом
GET /users/c1b01406-c3da-44d8-b8d0-61d5c7d045b6/

И PUT у вас получится сам по себе.
Точнее, сначала вам захочется сделать
POST /users\r\n\r\n{name: "Vasiliy", last_name: "Pupkin"}

Но потом к вам придут недовольные пользователи с вопросом про задвоение пользователей, и вы поймёте, что удобство от использования PUT перевешивает сомнительную экономию от переноса вызова CreateUUID на сервер.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: REST: прохой\хороший интерфейс
От: Cyberax Марс  
Дата: 10.02.20 07:57
Оценка:
Здравствуйте, Sinclair, Вы писали:

C>>Вот пример: "PUT /user\r\n\r\n{name: "Vasiliy", last_name: "Pupkin"}". Что тут происходит при двойном запуске? Будут созданы два пользователя или один пользователь?

S>Конечно же один. Читаем спецификацию HTTP, понимаем, что мы создаём ресурс по адресу /user.
S>Ровно его же мы получим обратно, выполнив GET /user
Я спецификацию HTTP знаю не то чтобы наизусть, но сильно лучше большинства людей, так как написал полностью соответствующую стандартам реализацию прокси для HTTP 1.1

В стандарте HTTP единственным способом сигнализации о создании нового объекта является код ответа HTTP. Этот код не является надёжным при сетевых сбоях. Так что повторный PUT по стандарту не является идемпотентным, так как НЕ МОЖЕТ вернуть 201 Created.

S>
S>PUT /users/c1b01406-c3da-44d8-b8d0-61d5c7d045b6/ \r\n\r\n{name: "Vasiliy", last_name: "Pupkin"}
S>

S>И отличать создание двух разных пользователей от повторных попыток создать одного и того же сервер будет легко.
Эта вот фигня "c1b01406-c3da-44d8-b8d0-61d5c7d045b6" и называется "ключ идемпотентности". В данном случае для этого используется внешний случайный ключ.

С тем же успехом можно сделать: "POST /users/operations\r\n\r\n{request_id: "2341234123412341234", op_name: "new", first_name: "Vasiliy", last_name: "Pupkin"}"

Более того, в данном случае так же возможна модель, когда что request_id вообще не сохраняется в постоянное хранилище и не является частью модели, а живёт на серверной стороне только в пределах некоторого retry-интервала.

S>Но потом к вам придут недовольные пользователи с вопросом про задвоение пользователей, и вы поймёте, что удобство от использования PUT перевешивает сомнительную экономию от переноса вызова CreateUUID на сервер.

Вы с какими-то странными мельницами воюете. Я ничего не говорил про экономию.
Sapienti sat!
Re[18]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.02.20 08:06
Оценка:
Здравствуйте, Cyberax, Вы писали:
C>Я спецификацию HTTP знаю не то чтобы наизусть, но сильно лучше большинства людей, так как написал полностью соответствующую стандартам реализацию прокси для HTTP 1.1
Я за вас очень рад
C>В стандарте HTTP единственным способом сигнализации о создании нового объекта является код ответа HTTP. Этот код не является надёжным при сетевых сбоях. Так что повторный PUT по стандарту не является идемпотентным, так как НЕ МОЖЕТ вернуть 201 Created.
Почему это он не может вернуть 201 Created?

S>>
S>>PUT /users/c1b01406-c3da-44d8-b8d0-61d5c7d045b6/ \r\n\r\n{name: "Vasiliy", last_name: "Pupkin"}
S>>

S>>И отличать создание двух разных пользователей от повторных попыток создать одного и того же сервер будет легко.
C>Эта вот фигня "c1b01406-c3da-44d8-b8d0-61d5c7d045b6" и называется "ключ идемпотентности". В данном случае для этого используется внешний случайный ключ.
Нет. Эта фигня называется "ID ресурса", а то, что она же используется в качестве ключа идемпотентности, это приятный бонус, полученный нами забесплатно.
C>С тем же успехом можно сделать: "POST /users/operations\r\n\r\n{request_id: "2341234123412341234", op_name: "new", first_name: "Vasiliy", last_name: "Pupkin"}"
С тем же, да не с тем. В случае с PUT идемпотентность операции дана нам стандартом; а в случае POST — навелосипедена. Т.е. я не могу написать никакого auto-retry механизма, который бы обрабатывал сбои самостоятельно и независимо от конкретного приложения.
C>Более того, в данном случае так же возможна модель, когда что request_id вообще не сохраняется в постоянное хранилище и не является частью модели, а живёт на серверной стороне только в пределах некоторого retry-интервала.
Вот именно. В PUT такая рисковая реализация невозможна. А вы предлагаете на ровном месте разложить грабли — клиент, который не смог записать результат POST к себе в базу (например, из-за переполнения диска) после починки прекрасно стартует с места "таак, вот мой request id, попробую-ка я ещё раз", и задвоит объект.
C>Вы с какими-то странными мельницами воюете. Я ничего не говорил про экономию.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: REST: прохой\хороший интерфейс
От: Teolog  
Дата: 10.02.20 08:21
Оценка:
Хороший интерфейс — а что, тут был интерфейс, вроде все просто работало.
Плохой интрефйейс — что не нравится этой сволочи, где код ошибки, и почему запрос нужно повторять 99 раз. Где чертова документация вообще. И домашний адрес автора, да.
Re[6]: REST: прохой\хороший интерфейс
От: Sharov Россия  
Дата: 10.02.20 14:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Sharov, Вы писали:


S>>>Так и тут — опытный архитектор сходу видит в системе то состояние, которое он будет реплицировать.

S>>А причем тут опыт, если понятно, что реплицировать надо всю бд, ибо это и есть состояние?
S>Ну конечно же нет. Вопрос в гранулярности этого состояния. Скажем, когда я бронирую себе конференцзал, мне нафиг не нужно состояние всех конференцзалов во всех офисах компании за бесконечный период.
S>Всё, что мне нужно — это интересующий меня диапазон в несколько дней.

Ну как же нет, когда таких пользователей может быть много, и мало ли какие записи ими будут востребованы? Репликация на уровне данных, master-slave, самый простой вариант.
Работает из коробки.


S>>А можно подробнее зачем в этой цепочке client ->http post -->server еще и прокси? Или он тут нужен для инвалидации кэша, тогда какая ему разница какой запрос (форма запроса), главное что post/resourceid?

S>Прокси, естественно, нужен для улучшения производительности. Смотрите: опять концепция REST даёт нам метаинформацию о том, что происходит. К примеру, можно всех пользователей офиса А запустить через прокси, и тогда как только один из них сходит на сервер за расписанием занятости ресурсов, остальные смогут получить ответ из локального прокси, а не через океан. Репликация состояния.

Это понятно, на пути POST запроса на кой там прокси, если только не для инвалидации?
Кодом людям нужно помогать!
Re[7]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.02.20 16:34
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Ну как же нет, когда таких пользователей может быть много, и мало ли какие записи ими будут востребованы?

Да вы смеётесь. Вот у вас, скажем, жалкие 40000 пользователей. Репликация всех изменений ко всем просто положит вам все каналы.

S>Репликация на уровне данных, master-slave, самый простой вариант.

S>Работает из коробки.
Эта репликация позволяет получать данные а) на чтение и б) с опозданием. Даже если она заработает для нужного нам масштаба, она никак не решает вопрос "а как мне внести изменения так, чтобы иметь детерминированный способ восстановления после сбоя".

S>Это понятно, на пути POST запроса на кой там прокси, если только не для инвалидации?

Для инвалидации конечно. По стандарту, прокси видя PUT реквест, должен считать кэш ресурса по его адресу устаревшим. Без дополнительного раундтрипа на апстрим.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[19]: REST: прохой\хороший интерфейс
От: Cyberax Марс  
Дата: 10.02.20 23:01
Оценка:
Здравствуйте, Sinclair, Вы писали:

C>>В стандарте HTTP единственным способом сигнализации о создании нового объекта является код ответа HTTP. Этот код не является надёжным при сетевых сбоях. Так что повторный PUT по стандарту не является идемпотентным, так как НЕ МОЖЕТ вернуть 201 Created.

S>Почему это он не может вернуть 201 Created?
Сценарий:
1. Создаём объект (с помощью PUT).
2. Сервер создаёт объект и возвращает 201.
3. Соединение рвётся, клиент ответ не получает.
4. Клиент пробует снова. Сервер ОБЯЗАН вернуть обычный 200 OK.

Т.е. PUT не идемпотентен по дизайну.

C>>Эта вот фигня "c1b01406-c3da-44d8-b8d0-61d5c7d045b6" и называется "ключ идемпотентности". В данном случае для этого используется внешний случайный ключ.

S>Нет. Эта фигня называется "ID ресурса", а то, что она же используется в качестве ключа идемпотентности, это приятный бонус, полученный нами забесплатно.
Это внешний ключ. Называть его можно как угодно, но суть не меняется.

Кроме того, он может быть неприменим по ряду причин:
1. Пользователям может хотеться приятный идентификатор, а не UUID.
2. Пользователям может требоваться гарантия невозможности утечки данных через специально сгенерированные клиентом UUID'ы.

C>>С тем же успехом можно сделать: "POST /users/operations\r\n\r\n{request_id: "2341234123412341234", op_name: "new", first_name: "Vasiliy", last_name: "Pupkin"}"

S>С тем же, да не с тем. В случае с PUT идемпотентность операции дана нам стандартом; а в случае POST — навелосипедена. Т.е. я не могу написать никакого auto-retry механизма, который бы обрабатывал сбои самостоятельно и независимо от конкретного приложения.
Стандарт ничего не обеспечивает, там только указаны требования к реализации ("станьте ёжиками").

C>>Более того, в данном случае так же возможна модель, когда что request_id вообще не сохраняется в постоянное хранилище и не является частью модели, а живёт на серверной стороне только в пределах некоторого retry-интервала.

S>Вот именно. В PUT такая рисковая реализация невозможна. А вы предлагаете на ровном месте разложить грабли — клиент, который не смог записать результат POST к себе в базу (например, из-за переполнения диска) после починки прекрасно стартует с места "таак, вот мой request id, попробую-ка я ещё раз", и задвоит объект.
Зато оно позволит избавиться от другого класса ошибок:
1. Клиент для оператора A делает PUT и создаёт объект. Но GPRS-модем — лопух, и аппаратура тоже. Так что ответ не приходит.
2. Оператор Б в офисе видит: "О! Пришёл новый пользователь! Сейчас я к нему детали из Linked-In добавлю!", — и таки добавляет детали.
3. Клиент для оператора А отвечает: "А вот хрен ты у меня чего-то добавишь!" И делает повторный PUT с начальными деталями, затирая изменения.

ЧСХ, в чистом HTTP проблема решения не имеет. If-Modified-Since и товарищи применимы по стандарту только к GET, и даже при его игнорировании всё равно недостаточны.

Понятно, что на практике её можно решить, вводя версирование или блокировки на уровне прикладного протокола.
Sapienti sat!
Re[20]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 11.02.20 03:15
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Сценарий:

C>1. Создаём объект (с помощью PUT).
C>2. Сервер создаёт объект и возвращает 201.
C>3. Соединение рвётся, клиент ответ не получает.
C>4. Клиент пробует снова. Сервер ОБЯЗАН вернуть обычный 200 OK.
Нет. Если сервер детектит сохранение того же объекта, он имеет право вернуть 201. Вы недостаточно внимательно читали спецификацию. Как раз идемпотентность от PUT там потребована в самом начале
C>Это внешний ключ. Называть его можно как угодно, но суть не меняется.
Совершенно верно. В REST у всех ресурсов есть уникальные URL.

C>1. Пользователям может хотеться приятный идентификатор, а не UUID.

Не проблема — можно использовать приятный идентификатор. Правда, с ним выше риск получить 409 на ровном месте.
C>2. Пользователям может требоваться гарантия невозможности утечки данных через специально сгенерированные клиентом UUID'ы.
Не проблема — клиенты могут использовать любую технологию порождения ID. Не хотят утечки данных — пусть не используют их при генерации UUID.
З.Ы. Вы в курсе, что MAC адрес в гражданских реализациях генераторов GUID не используется уже больше десяти лет?

C>Стандарт ничего не обеспечивает, там только указаны требования к реализации ("станьте ёжиками").

Погодите. Вы что, не понимаете, как именно реализовать идемпотентность? Тогда всё ещё хуже, чем я думал. Ешё раз поясню: RPC не содержит даже требования идемпотентности. Это означает, что архитекторы (большинство из которых квалифицированы менее, чем вы) просто забьют на них.
Вот вы берёте REST, и несмотря на подробное изложение требований к API ухитряетесь делать вид, что идемпотентность — это какая-то rocket science.
S>>Вот именно. В PUT такая рисковая реализация невозможна. А вы предлагаете на ровном месте разложить грабли — клиент, который не смог записать результат POST к себе в базу (например, из-за переполнения диска) после починки прекрасно стартует с места "таак, вот мой request id, попробую-ка я ещё раз", и задвоит объект.
C>Зато оно позволит избавиться от другого класса ошибок:
C>1. Клиент для оператора A делает PUT и создаёт объект. Но GPRS-модем — лопух, и аппаратура тоже. Так что ответ не приходит.
C>2. Оператор Б в офисе видит: "О! Пришёл новый пользователь! Сейчас я к нему детали из Linked-In добавлю!", — и таки добавляет детали.
C>3. Клиент для оператора А отвечает: "А вот хрен ты у меня чего-то добавишь!" И делает повторный PUT с начальными деталями, затирая изменения.
Отлично. И как этот же сценарий выглядит в подходе с POST и кратковременным requestID? У нас есть два варианта:
— requestID протух. Клиент А, очевидно, создаст дубля.
— requestID ещё жив. Что получит клиент А?
C>ЧСХ, в чистом HTTP проблема решения не имеет. If-Modified-Since и товарищи применимы по стандарту только к GET, и даже при его игнорировании всё равно недостаточны.
Имеет. Для начала, нужно понять, что проблема lost update в принципе не связана с созданием. У нас два глухих клиента могут постить чередующиеся конфликтующие апдейты, независимо от выбора глаголов и вообще подхода к проектированию API.
А как только вы решите эту проблему, то и обработка создания объектов проблемы у вас не вызовет.
Далее, можно всё же прочитать спецификацию, и найти, что для нашего случая, помимо версии на уровне прикладного протокола, есть штатный механизм.
Если клиент хочет быть уверен, что он именно создаёт новый ресурс, а не затирает чужой, то

If-None-Match can also be used with a value of "*" to prevent an
unsafe request method (e.g., PUT) from inadvertently modifying an
existing representation of the target resource when the client
believes that the resource does not have a current representation
(Section 4.2.1 of [RFC7231]). This is a variation on the "lost
update" problem that might arise if more than one client attempts to
create an initial representation for the target resource.

Аналогично можно решить проблему конкурирующих PUT, которая возникает даже при идеальном качестве связи. Только применять надо If-Match.
C>Понятно, что на практике её можно решить, вводя версирование или блокировки на уровне прикладного протокола.
Можно. С версионированием работать может оказаться удобнее, чем в голых хидерах. В частности, оно позволяет лёгким манием руки прикрутить хранение всей истории модификаций. Но это не отменяет того факта, что голый HTTP 1.1 предусматривает решение множества проблем, которые вам кажутся неразрешимыми.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: REST: прохой\хороший интерфейс
От: samius Япония http://sams-tricks.blogspot.com
Дата: 11.02.20 04:38
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, samius, Вы писали:

S>>Поставил "супер", но вот тут я принципиально не согласен. Не нужно противопоставлять RPC и REST вовсе. RPC — технология, позволяющая выполнить удаленный вызов и точка. REST — это архитектурный стиль организации работы распределенных приложений и сервисов. Это как сравнивать преимущества правил дорожного движения над автомобилем.
S>Под словом RPC понимают как минимум три вещи:
S>1. Методика, при которой у нас вызов, выглядяший в клиентском коде как обычный вызов процедуры, под капотом вызывает выполнение кода на удалённом сервисе
Я об этом.
S>2. Конкретный стандарт RPC типа XML-RPC, Corba, COM
Допускаю, но в этом случае нужен определенный контекст, например, введенный упоминанием конкретного стандарта.
S>3. Стиль разработки API, в котором наружу выставляются "методы", у которых нет какой-либо семантики, кроме прикладной.
Это субъективно. Этот же самый стиль вполне может быть назван REST. Есть тому масса примеров, и даже литература о REST вовсе без упоминания о сбоях и идемпотентности.

S>В конкретных примерах у нас зачастую все три соединяются в одном — например, у нас есть API без метаинформации о поведении методов, реализованный по стандарту XML-RPC, и к нему написана библиотека на клиентском языке, которая оборачивает вызовы эндпоинта в "процедуры".


Этого достаточно, что бы выдавать свой API за REST.

S>Чисто теоретически, можно взять п.1, и приколхозить к нему инструменты для восстановления после сбоев.

S>Например, у всех get-методов можно добавить параметр "lastResponseTimestamp", а помимо прямого результата научить метод возвращать аналоги 304 Not Modified, а также передавать timestamp ответа. Потом можно прикрутить дельта-енкодинг.
S>Потом — компрессию. Потом прикрутить graceful degradation в зависимости от client capabilities, потому что у нас уже четыре версии протокола, и мы ни одну из них не можем заблокировать, т.к. под них всё ещё есть клиенты, которых мы не можем заставить проапгрейдиться.
S>У модифицирующих методов можно прикрутить ключи идемпотентности.

S>По факту, изо всего этого делают только ключи идемпотентности, да и то не всегда. Потому что технология диктует. Это как плыть против ветра — в REST это всё делается легко и естественно, и с гарантией обратной совместимости. В RPC — хренушки.


Об этом вообще мало кто заморачивается. Вот, наткнулся не так давно...
https://labs.ig.com/rest-trading-api-reference/service-detail?id=542
Вытащили наружу обернутые POST-ом методы, даже забыв идентификатор ресурса в теле запроса. И представили под вывеской REST.

У меня вообще создается впечатление, что попытки отличать "REST" от "RPC" по наличию механизмов восстановления присущи лишь местному клубу.
Re[7]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 11.02.20 05:18
Оценка:
Здравствуйте, samius, Вы писали:

S>У меня вообще создается впечатление, что попытки отличать "REST" от "RPC" по наличию механизмов восстановления присущи лишь местному клубу.

Нет. Исходно аббревиатура ReST означает "Representational State Transfer", и введена в диссертации Филдинга от 2000 года. Дело даже не в наличии механизмов восстановления, а в самой философии API.
RESTful API сразу проектируется так, что у нас есть некоторое "состояние" разделяемых данных, и мы можем это состояние получить через вызовы GET/HEAD и влиять на него при помощи PUT/PATCH/DELETE/POST.
То, что при этом у нас появляются обобщённые подходы к обработке сбоев — это побочный эффект такого выбора. Наряду с кэшированием, компрессией, частичной передачей и т.п.
По большому счёту, REST можно изобрести и независимо от HTTP. Я подозреваю, что механизм синхронизации баз в Lotus Notes/Domino, реализует что-то типа того же REST.
Или, например, можно запилить REST поверх SQL, выставив набор view с instead-of триггерами. Т.е. все операции с базой будут идти не через хранимки, а через DML (select/insert/update/delete).

Там приятным побочным эффектом будет, к примеру, возможность "отгрузить" сразу пачку ордеров без cross apply и прочей магии:
update orders set status = 'shipped' from orders inner join delivery_lines dl on orders.id = dl.order_id where dl.id = @deliveryId


К сожалению, с публикаций первоисточников прошло много времени. В интернет набежала толпа недоумков, которые слышали про REST только в пересказе других недоумков. Они думают, что REST — это любой JSON over HTTP.
Не надо их читать. Надо читать классику типа http://shop.oreilly.com/product/9780596529260.do.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: REST: прохой\хороший интерфейс
От: samius Япония http://sams-tricks.blogspot.com
Дата: 11.02.20 06:49
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, samius, Вы писали:


S>>У меня вообще создается впечатление, что попытки отличать "REST" от "RPC" по наличию механизмов восстановления присущи лишь местному клубу.

S>Нет. Исходно аббревиатура ReST означает "Representational State Transfer", и введена в диссертации Филдинга от 2000 года. Дело даже не в наличии механизмов восстановления, а в самой философии API.
S>RESTful API сразу проектируется так, что у нас есть некоторое "состояние" разделяемых данных, и мы можем это состояние получить через вызовы GET/HEAD и влиять на него при помощи PUT/PATCH/DELETE/POST.
S>То, что при этом у нас появляются обобщённые подходы к обработке сбоев — это побочный эффект такого выбора. Наряду с кэшированием, компрессией, частичной передачей и т.п.
S>По большому счёту, REST можно изобрести и независимо от HTTP. Я подозреваю, что механизм синхронизации баз в Lotus Notes/Domino, реализует что-то типа того же REST.

Сам Филдинг упоминает о том, что РЕСТ основан на совокупности стилей, перечисленных в главе 3 его диссертации. Один из — Client-Stateless-Server (CSS), описанный в 1994-м (https://www.ics.uci.edu/~fielding/pubs/dissertation/net_arch_styles.htm). РЕСТ — всего лишь гибрид. В любом случае, Филдинг не преподносит его как изобретение. Объединение, наследование — да. И по сути этого объединения вышло так, что работа с состоянием не выглядит в этом объединении ключевой. Безусловно является, но не выглядит таковой. Замылилась репрезентативностью.

S>Или, например, можно запилить REST поверх SQL, выставив набор view с instead-of триггерами. Т.е. все операции с базой будут идти не через хранимки, а через DML (select/insert/update/delete).

Запилить можно.

S>Там приятным побочным эффектом будет, к примеру, возможность "отгрузить" сразу пачку ордеров без cross apply и прочей магии:

S>
S>update orders set status = 'shipped' from orders inner join delivery_lines dl on orders.id = dl.order_id where dl.id = @deliveryId
S>


S>К сожалению, с публикаций первоисточников прошло много времени. В интернет набежала толпа недоумков, которые слышали про REST только в пересказе других недоумков. Они думают, что REST — это любой JSON over HTTP.

S>Не надо их читать. Надо читать классику типа http://shop.oreilly.com/product/9780596529260.do.

Читать — это хорошо. Свой API можно сделать правильно и избежать некоторое кол-во проблем. Проблема-то несколько в другом. Есть некоторое кол-во сервисов, предоставляющих REST API в духе JSON over HTTP (не вдаваясь в математику HTTP). Как клиенту мне сложно с ними дискутировать о том, каких недоумков им не нужно читать, проектируя REST API. Дешевле закостылить клиента, чем сподвигнуть сервис на переписывание API по фен-шую.
Хочется сказать им: ребята, надо делать вот так ... Но у Филдинга слово "идемпотентность" в работе не упоминается, а на каждую книжку, где оно упоминается вместе с REST, найдется пяток таких книг, где его нет.
Re[9]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 11.02.20 07:54
Оценка: 80 (4) :)
Здравствуйте, samius, Вы писали:

S>Сам Филдинг упоминает о том, что РЕСТ основан на совокупности стилей, перечисленных в главе 3 его диссертации. Один из — Client-Stateless-Server (CSS), описанный в 1994-м (https://www.ics.uci.edu/~fielding/pubs/dissertation/net_arch_styles.htm). РЕСТ — всего лишь гибрид. В любом случае, Филдинг не преподносит его как изобретение. Объединение, наследование — да. И по сути этого объединения вышло так, что работа с состоянием не выглядит в этом объединении ключевой. Безусловно является, но не выглядит таковой. Замылилась репрезентативностью.

Эмм, не знаю насчёт "замылилась". У Филдинга representational означает, что мы не лезем в состояние сервера напрямую. Всё, что у нас есть — некоторое представление этого состояния; и вся коммуникация между клиентом и сервером выражена в терминах этого представления.
И, с точки зрения Филдинга, REST — первичен, как абстрактная модель. HTTP 1.1 — это, с его точки зрения, всего лишь пример применения концепции REST к World Wide Web. (Глава 6.3).

S>>К сожалению, с публикаций первоисточников прошло много времени. В интернет набежала толпа недоумков, которые слышали про REST только в пересказе других недоумков. Они думают, что REST — это любой JSON over HTTP.

S>>Не надо их читать. Надо читать классику типа http://shop.oreilly.com/product/9780596529260.do.

S>Читать — это хорошо. Свой API можно сделать правильно и избежать некоторое кол-во проблем. Проблема-то несколько в другом. Есть некоторое кол-во сервисов, предоставляющих REST API в духе JSON over HTTP (не вдаваясь в математику HTTP). Как клиенту мне сложно с ними дискутировать о том, каких недоумков им не нужно читать, проектируя REST API. Дешевле закостылить клиента, чем сподвигнуть сервис на переписывание API по фен-шую.

Это понятно. Мы не можем изменить прошлое.
S>Хочется сказать им: ребята, надо делать вот так ... Но у Филдинга слово "идемпотентность" в работе не упоминается, а на каждую книжку, где оно упоминается вместе с REST, найдется пяток таких книг, где его нет.
Ссылайтесь на книжку Руби и Ричардсона. Она — правильная. И доступна бесплатно.
Вот прямо сейчас мы изменяем будущее — этот форум читает человек пять, из них, может быть, 2% будут когда-то иметь отношение к проектированию веб-сервисов.
Важно, чтобы хотя бы они умели отличать хорошее от плохого. Нужно вовремя им показать правильную книжку.
И быть готовыми отвечать на каверзные вопросы — а то вон, оказывается, можно даже собственный прокси-сервер реализовать, так и не поняв, как REST и HTTP помогают дизайнить хорошие API.

По хорошему, надо расширять евангелическую деятельность. В надежде, что пропаганда однажды зацепит людей в Редмонде, Купертино, и прочих Менло Парках.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: REST: прохой\хороший интерфейс
От: Pavel Dvorkin Россия  
Дата: 11.02.20 11:26
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Чисто теоретически, можно взять п.1, и приколхозить к нему инструменты для восстановления после сбоев.

S>Например, у всех get-методов можно добавить параметр "lastResponseTimestamp", а помимо прямого результата научить метод возвращать аналоги 304 Not Modified, а также передавать timestamp ответа.

Можно и проще, как это сделано, например , в thrift. Сервер при ошибке выбрасывает исключение, которое портируется на клиент и представляется там на языке программирования клиента. Так что разницы между RPC и LPC практически нет в этом плане.
With best regards
Pavel Dvorkin
Re[10]: REST: прохой\хороший интерфейс
От: Sharov Россия  
Дата: 11.02.20 12:14
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Эмм, не знаю насчёт "замылилась". У Филдинга representational означает, что мы не лезем в состояние сервера напрямую. Всё, что у нас есть — некоторое представление этого состояния; и вся коммуникация между клиентом и сервером выражена в терминах этого представления.


А что значит состояние сервера, значение регистров цпу + озу, бд? Почему используется слово "представление", а не слово "часть" данных.


S>И, с точки зрения Филдинга, REST — первичен, как абстрактная модель. HTTP 1.1 — это, с его точки зрения, всего лишь пример применения концепции REST к World Wide Web. (Глава 6.3).


Фундаментальнее скорее, ибо полиморфизм (get,post..) во весь рост, но вдохновлялся http.
Кодом людям нужно помогать!
Re[7]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 12.02.20 04:02
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:
S>>Например, у всех get-методов можно добавить параметр "lastResponseTimestamp", а помимо прямого результата научить метод возвращать аналоги 304 Not Modified, а также передавать timestamp ответа.
PD>Можно и проще, как это сделано, например , в thrift. Сервер при ошибке выбрасывает исключение, которое портируется на клиент и представляется там на языке программирования клиента. Так что разницы между RPC и LPC практически нет в этом плане.
Что делать при ошибке — понятно. Можно даже заставить все исключения наследоваться от RetryableException либо от FatalException.
Непонятно, как реализовать responseTimeStamp и 304 Not Modified. Это не ошибка, а сигнал о том, что данные не изменились.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: REST: прохой\хороший интерфейс
От: Sinclair Россия https://github.com/evilguest/
Дата: 12.02.20 04:07
Оценка: 15 (1)
Здравствуйте, Sharov, Вы писали:

S>А что значит состояние сервера, значение регистров цпу + озу, бд?

Это значит, что одинаковый запрос может приводить к разным результатам. Технически — да, содержание регистров, RAM, HDD, и прочих локальных ресурсов.
S>Почему используется слово "представление", а не слово "часть" данных.
Потому, что речь не только о "проекции", но и о трансформации.
Самый простой пример — сервер возвращает "текущее время". Внутри оно хранится как, например, unixtime. Но клиент видит его как ISO 8601.
Более жизненный пример — ресурс "заказ в интернет-магазине" представлен на стороне сервера как содержимое десятка таблиц, а клиент видит его как единый JSON-документ. И модифицирует как единый JSON-документ.
Примерно такое же отличие, как между property и field в C#/Delphi: состояние объекта хранится в полях, но читать и изменять его можно только через свойства.
S>>И, с точки зрения Филдинга, REST — первичен, как абстрактная модель. HTTP 1.1 — это, с его точки зрения, всего лишь пример применения концепции REST к World Wide Web. (Глава 6.3).
S>Фундаментальнее скорее, ибо полиморфизм (get,post..) во весь рост, но вдохновлялся http.
В тексте диссертации прямым текстом написано, что при проектировании HTTP 1.1 применялся REST
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 12.02.2020 5:57 Sinclair . Предыдущая версия .
Re[8]: REST: прохой\хороший интерфейс
От: Pavel Dvorkin Россия  
Дата: 12.02.20 11:44
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Что делать при ошибке — понятно. Можно даже заставить все исключения наследоваться от RetryableException либо от FatalException.

S>Непонятно, как реализовать responseTimeStamp и 304 Not Modified. Это не ошибка, а сигнал о том, что данные не изменились.

Мне кажется, что ты пытаешься навязать RPC психологию HTTP, отсюда все проблемы , в частности, желание получить 304

304 Not Modified — сервер возвращает такой код, если клиент запросил документ методом GET, использовал заголовок If-Modified-Since или If-None-Match и документ не изменился с указанного момента.

Да, для HTTP это имеет смысл. Но вот тебе иной пример

Я не буду, конечно, утверждать, что клиент РБД связывается с ней по именно по RPC. Там внизу скорее всего иначе. Но суть от этого не меняется.

Запросил ты что-то с помощью SELECT и что ? Кто там не изменился и что должно возвращаться ? Нет такого понятия — не изменился. Просил — получи, а потом сам и разбирайся, что там изменилось, а что нет. Да еще учти, что пока разбираешься, там что-то могло измениться. Хочешь знать, когда изменялось — предусмотри в таблице timestamp.

А 304... Ну что 304 ? Придуман во времена, когда ресурсы были в основном статическими, а If-Modified-Since реализовать было предельно просто : взять время последней модификации файла, и все дела.

А потом начали HTTP, который вовсе не был предназначен изначально для интерфейса со сложным серверным кодом, применять именно для этой цели. Понятно, что без проблем не обошлось.
With best regards
Pavel Dvorkin
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.