Долгоживущие RESTful-взаимодействия

В настоящее время в моей команде идет обсуждение, и мне было бы интересно узнать другие точки зрения. Предположим, у нас есть веб-служба RESTful, роль которой заключается в аннотировании документов с применением различных алгоритмов и служб анализа. Основное взаимодействие понятно: у нас есть ресурс, который представляет собой коллекцию документов; клиент отправляет новый документ в коллекцию, возвращает URI нового документа, а затем может ПОЛУЧИТЬ этот docURI для возврата документа или ПОЛУЧИТЬ {docURI}/metadata для просмотра общих метаданных, {docURI}/ne для именованных сущностей и т. д. Проблема в том, что некоторые выполнение анализов может занять много времени. Предположим, что клиент ПОЛУЧАЕТ URI метаданных до завершения анализа, потому что он хочет иметь возможность отображать частичные или добавочные результаты в пользовательском интерфейсе. Повторение GET в будущем может дать больше результатов.

Решения, которые мы обсуждали, включают:

  • сохранение HTTP-соединения открытым до тех пор, пока не будут выполнены все анализы (что не кажется масштабируемым)
  • использование заголовков content-length и accept-range для получения добавочного контента (но мы не знаем заранее, какой длины будет окончательный контент)
  • предоставление канала Atom для каждого ресурса, чтобы клиент подписывался на события обновления, а не просто ПОЛУЧАЛ ресурс (кажется слишком сложным и, возможно, ресурсоемким, если есть много активных документов)
  • просто GET возвращает все, что доступно в данный момент (но это по-прежнему оставляет проблему того, что клиент узнает, когда мы, наконец, закончим) [отредактировано для удаления ссылки на идемпотентность после комментариев].

Любые мнения или предложения по альтернативным способам обработки долгоживущих или асинхронных взаимодействий в архитектуре RESTful?

Ян


person Ian Dickinson    schedule 23.09.2008    source источник
comment
Два решения на самом деле. Простое решение: используйте GET, верните все метаданные, которые у вас есть на тот момент, установите заголовок времени жизни кеша на ноль, если эти данные все еще генерируются. Комплексное решение: используйте push для отправки метаданных клиентам по мере их создания ... imo грязно, лаваш, не стоит. Используйте GET.   -  person thecoshman    schedule 23.07.2015


Ответы (7)


Я бы реализовал это следующим образом:

1) клиент запрашивает метаданные
2) сервер возвращает либо фактические данные (если они уже доступны), либо маркер NotReady
3) клиент спрашивает сервер, когда данные будут доступны (этот шаг можно объединить с предыдущим)
4 ) сервер возвращает временной интервал (могут быть некоторые эвристики для общего количества выполняемых заданий и т. д.)
5) клиент ждет указанный период времени и переходит к шагу 1

Таким образом, вы можете предоставлять данные клиентам как можно быстрее. Вы можете формировать нагрузку на сервер, настраивая интервал задержки, возвращаемый на шаге 4)

person aku    schedule 23.09.2008
comment
Спасибо за ответ. Это будет иметь данные, доступные для клиента сразу, когда они будут готовы, тогда как я хотел бы постепенно представлять данные в пользовательском интерфейсе по мере их появления. - person Ian Dickinson; 23.09.2008
comment
Ян Дикинсон, не обязательно. Вы можете вернуть частичные данные, но пометить их как неполные и указать интервал опроса. - person aku; 23.09.2008
comment
@aku Также, возможно, с использованием экспоненциального отката? - person Hassan Ahmed; 22.01.2021

предоставление канала Atom для каждого ресурса, чтобы клиент подписывался на события обновления, а не просто ПОЛУЧАЛ ресурс (кажется слишком сложным и, возможно, ресурсоемким, если есть много активных документов)

Рассматривали ли вы SUP?

Если возможен опрос, зачем заморачиваться с фидом? Почему бы клиентам просто не опросить сам ресурс?

Не могли бы вы сократить количество ненужных опросов, включив расчетное время завершения анализа?

person Jim    schedule 23.09.2008
comment
Привет, Джим, спасибо за указание на SUP - я почитаю. Причина рассмотрения канала, а не прямого опроса, заключается в том, что клиенту будет проще определить последние изменения и по-разному представить их в пользовательском интерфейсе. - person Ian Dickinson; 23.09.2008
comment
Очевидно, вы бы знали лучше, чем я, но я бы не подумал, что клиенту будет сложно идентифицировать обновленные поля. Наверняка они могут сравнить полученные данные с тем, что у них уже есть? - person Jim; 23.09.2008
comment
Да, клиент может сам провести сравнение (результаты представляют собой RDF-документы, а не поля, но точка остается в силе). Я еще не привержен тому или иному, но просто хотел упомянуть одну причину, по которой фид может иметь преимущество. Специальные коллажи могут быть еще одним, хотя я еще не убежден! - person Ian Dickinson; 23.09.2008

Используйте HTTP 202 Accepted.

Кроме того, ознакомьтесь с веб-сервисами RESTful — здесь я узнал о вышеизложенном. .

person Hank Gay    schedule 23.09.2008
comment
По той же ссылке кажется применимым 206 Partial Content. - person Jeroen Wyseur; 23.09.2008
comment
@JeroenWyseur: Нет, запрос ДОЛЖЕН включать поле заголовка Range. - person user359996; 14.10.2010

  • просто игнорируя идемпотентность и заставляя GET возвращать все, что доступно в данный момент (но это по-прежнему оставляет проблему клиента, знающего, когда мы, наконец, закончим).

Действительно ли GET, который со временем возвращает разные результаты, означает, что он не является идемпотентным? В спецификации указано:

Методы также могут иметь свойство «идемпотентности» в том смысле, что (помимо ошибок или проблем с истечением срока действия) побочные эффекты N> 0 идентичных запросов такие же, как и для одного запроса.

То есть несколько вызовов GET могут возвращать разные результаты, если сами вызовы не имеют побочных эффектов.

В этом случае, возможно, ваш метод REST может использовать условный механизм GET и кэширования, чтобы указать, когда это будет сделано:

  • While the analysis is in progress, a GET {docURI}/metadata response could have:
    • an Expires header set to a few seconds in the future.
    • нет заголовка ETag.
  • Once the analysis is done, responses for that resource have:
    • no Expires header.
    • ETag. Последующие запросы с ETag должны возвращать 304 Not Modified.

NB вы можете рассмотреть другие заголовки ответа, связанные с кэшированием, а не только Expires.

Это «ощущается» как дизайн RESTful — вы можете представить, что веб-браузер делает правильные вещи, выполняя последовательные запросы к этому ресурсу.

person Matt Quail    schedule 23.09.2008
comment
Мэтт (и Джим тоже) - Да, вы правы насчет идемпотентности, спасибо за пояснение. Я отредактирую свой вопрос, чтобы удалить этот потенциальный источник путаницы! - person Ian Dickinson; 23.09.2008

«Просто иметь GET, возвращающий все, что доступно в то время», имеет массу смысла. За исключением того, что когда они проводят опрос, вы не хотите продолжать возвращать то, что они уже знают. Ответы становятся длиннее с каждым опросом.

Вам нужно, чтобы они предоставили вам свое «то, что я видел до сих пор» в запросе GET. Это дает вам идемпотентность. Если они просят чанк 1, они всегда получают один и тот же ответ. Как только они увидят кусок 1, они могут попросить кусок 2.

Ответ не становится больше. Становится доступно больше частей. GET "на уровне коллекции" предоставляет размер ответа. У вас есть GET на уровне детализации для каждой доступной части.

По сути, это алгоритм, аналогичный подтверждению TCP/IP. Когда они принимают кусок, вы отправляете следующий кусок. Если есть следующий кусок, в противном случае вы отправляете 200-ничего нового в отчете.

«Проблема, связанная с тем, что клиент знает, когда мы, наконец, закончим», не имеет значения. Они не могут знать, и вы не можете предсказать, сколько времени это займет.

Вы не хотите, чтобы они занимались «занятым ожиданием» — опросом, чтобы узнать, закончили ли вы еще — это довольно большая нагрузка на ваш сервер. Если они нетерпеливы. Вы можете ограничить их запросы. Вы можете отправить им «проверить через x секунд», где x будет постепенно увеличиваться.

Вы даже можете использовать алгоритм планировщика в стиле Unix, где их счет снижается, когда они опрашивают, и повышается, если они не опрашивают в течение X секунд.

Альтернативой является своего рода очередь, в которой вы отправляете им результаты. Для этого они должны предоставить URI, который вы можете отправить, чтобы сообщить им, что вы закончили.

Или они используют Atom для облегченной архитектуры опроса. Хотя Atom кажется сложным — и он по-прежнему включает в себя опрос — вы предоставляете минимальный ответ Atom («еще не изменен»), пока не закончите, когда вы предоставляете («новые результаты»), чтобы они могли выполнить настоящую работу. тяжелый вес. Это для «все или ничего», а не для описанной выше техники инкрементного ответа.

Вы также можете думать о GET «уровня коллекции» как о своем статусе Atom в процессе в целом.

person S.Lott    schedule 23.09.2008
comment
Что касается того, что вы не хотите продолжать возвращать то, что они уже знают, HTTP уже позаботится об этом с ответами 304. Что касается того, что я видел до сих пор, HTTP уже позаботился об этом с помощью If-None-Match. Вы заново изобретаете колесо. - person Jim; 24.09.2008
comment
Не заново изобретая. Это прекрасные способы кодирования результатов. Тем не менее, вам все равно нужно вычислить результаты, определить, что нужно знать клиенту, чтобы вы могли выбрать между 200 или 304. - person S.Lott; 25.09.2008

Одним из альтернативных решений, которое может подойти или не подойти в вашем случае, является добавление новой конечной точки под названием «AnnotationRequests». Отправьте документ (или ссылку на него) в конечную точку AnnotationRequests, и он должен вернуть местоположение (например, http://example.org/AnnotationRequest/2042), что позволит вашему клиенту опрашивать статус процесса. По завершении процесса представление AnnotationRequest может содержать ссылку на готовый документ.

Одним приятным побочным эффектом этого является то, что вы можете выполнить GET для AnnotationRequests, чтобы увидеть документы, которые в данный момент обрабатываются. Вам решать, как долго вы хотите хранить AnnotationRequests. Может быть полезно вести полную историю того, когда они были запрошены, кем и сколько времени они заняли, или можно периодически выбрасывать их.

person Darrel Miller    schedule 24.09.2008

Вы можете проверить Уди Дахана nServiceBus.

person David Robbins    schedule 26.09.2008
comment
Выглядит интересно. Кто-нибудь знает эквивалент Java? - person Ian Dickinson; 26.09.2008