Вопрос:

Транзакции в REST?

rest

66676 просмотра

13 ответа

45950 Репутация автора

Мне интересно, как бы вы реализовали следующий вариант использования в REST. Можно ли вообще обойтись без ущерба для концептуальной модели?

Прочитайте или обновите несколько ресурсов в рамках одной транзакции. Например, переведите 100 долларов с банковского счета Боба на счет Джона.

Насколько я могу судить, единственный способ реализовать это - обман. Вы можете отправить POST к ресурсу, связанному с Джоном или Бобом, и выполнить всю операцию, используя одну транзакцию. Насколько я понимаю, это нарушает архитектуру REST, потому что вы, по сути, туннелируете вызов RPC через POST, а не работаете с отдельными ресурсами.

Автор: Gili Источник Размещён: 29.09.2008 01:33

Ответы (13)


9 плюса

26613 Репутация автора

Вы должны были бы свернуть свой собственный «идентификатор транзакции» тип управления TX. Так было бы 4 звонка:

http://service/transaction (some sort of tx request)
http://service/bankaccount/bob (give tx id)
http://service/bankaccount/john (give tx id)
http://service/transaction (request to commit)

Вам придется обрабатывать сохранение действий в БД (если балансировка нагрузки) или в памяти или что-то подобное, а затем обрабатывать коммит, откат, тайм-аут.

Не очень хороший день в парке.

Автор: TheSoftwareJedi Размещён: 29.09.2008 01:52

-3 плюса

20515 Репутация автора

Я думаю, вы могли бы включить TAN в URL / ресурс:

  1. PUT / транзакция для получения идентификатора (например, «1»)
  2. [PUT, GET, POST, что угодно] / 1 / account / bob
  3. [PUT, GET, POST, что угодно] / 1 / счет / счет
  4. УДАЛИТЬ / транзакция с ID 1

Просто идея.

Автор: Till Размещён: 29.09.2008 02:01

87 плюса

118929 Репутация автора

Решение

Рассмотрим сценарий корзины покупок RESTful. Корзина покупок является концептуально вашей упаковкой транзакций. Точно так же, как вы можете добавить несколько товаров в корзину покупок, а затем отправить эту корзину для обработки заказа, вы можете добавить запись учетной записи Боба в оболочку транзакции, а затем запись учетной записи Билла в оболочку. Когда все части на месте, то вы можете POST / PUT оболочки транзакции со всеми компонентами компонентов.

Автор: Darrel Miller Размещён: 29.09.2008 02:13

2 плюса

29066 Репутация автора

Я думаю, что в этом случае вполне приемлемо нарушить чистую теорию REST в этой ситуации. В любом случае, я не думаю, что на самом деле в REST есть что-то, что говорит о том, что вы не можете касаться зависимых объектов в тех случаях, когда это требуется.

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

Автор: Toby Hede Размещён: 29.09.2008 02:35

0 плюса

381 Репутация автора

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

Таким образом, для перехода между <url-base>/account/aи <url-base>/account/bвы можете опубликовать следующее <url-base>/transfer.

<Передача>
    <С>  / счет / а 
    <В>  / счет / б 
    <Сумма> 50 

Это создаст новый ресурс передачи и вернет новый URL-адрес передачи - например <url-base>/transfer/256.

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

Это, однако, не распространяется на распределенную транзакцию (если, скажем, «a» хранится в одном банке за одной услугой, а «b» - в другом банке за другой услугой), за исключением того, чтобы сказать «попытаться сформулировать все» операции способами, которые не требуют распределенных транзакций ".

Автор: Phasmal Размещён: 08.10.2010 03:37

56 плюса

3947 Репутация автора

Есть несколько важных случаев, на которые этот вопрос не получил ответа, и я думаю, что это слишком плохо, потому что он имеет высокий рейтинг в Google по поисковым запросам :-)

В частности, было бы неплохо: если вы выполняете POST дважды (из-за того, что какой-то кеш работает в промежуточном состоянии), вы не должны передавать сумму дважды.

Чтобы добраться до этого, вы создаете транзакцию как объект. Он может содержать все данные, которые вы уже знаете, и переводить транзакцию в состояние ожидания.

POST /transfer/txn
{"source":"john's account", "destination":"bob's account", "amount":10}

{"id":"/transfer/txn/12345", "state":"pending", "source":...}

Как только у вас есть эта транзакция, вы можете зафиксировать ее, например:

PUT /transfer/txn/12345
{"id":"/transfer/txn/12345", "state":"committed", ...}

{"id":"/transfer/txn/12345", "state":"committed", ...}

Обратите внимание, что множественные путы на данном этапе не имеют значения; даже GET на TXN вернет текущее состояние. В частности, второй PUT обнаружит, что первый уже находится в соответствующем состоянии, и просто вернет его - или, если вы попытаетесь перевести его в состояние «отката» после того, как он уже находится в «зафиксированном» состоянии, вы получите ошибка, и фактическая совершенная транзакция возвращается.

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

Автор: Jon Watte Размещён: 19.01.2011 10:46

31 плюса

327 Репутация автора

В терминах REST ресурсы - это существительные, на которые можно воздействовать с помощью глаголов CRUD (создание / чтение / обновление / удаление). Поскольку глагола «перевод денег» не существует, нам необходимо определить ресурс «транзакции», на который можно воздействовать с помощью CRUD. Вот пример в HTTP + POX. Первый шаг - СОЗДАНИЕ (метод HTTP POST) новой пустой транзакции:

POST /transaction

Это возвращает идентификатор транзакции, например «1234» и в соответствии с URL «/ транзакция / 1234». Обратите внимание, что запуск этого POST несколько раз не приведет к созданию одной и той же транзакции с несколькими идентификаторами, а также позволит избежать введения состояния ожидания. Кроме того, POST не всегда может быть идемпотентным (требование REST), поэтому обычно рекомендуется минимизировать данные в POST.

Вы можете оставить генерацию идентификатора транзакции клиенту. В этом случае вам потребуется POST / транзакция / 1234 для создания транзакции «1234», и сервер вернет ошибку, если она уже существует. В ответе об ошибке сервер может вернуть текущий неиспользуемый идентификатор с соответствующим URL. Не стоит запрашивать у сервера новый идентификатор с помощью метода GET, поскольку GET никогда не должен изменять состояние сервера, а создание / резервирование нового идентификатора изменяет состояние сервера.

Далее мы ОБНОВЛЯЕМ (метод PUT HTTP) транзакцию со всеми данными, неявно фиксируя ее:

PUT /transaction/1234
<transaction>
  <from>/account/john</from>
  <to>/account/bob</to>
  <amount>100</amount>
</transaction>

Если транзакция с идентификатором «1234» ранее была PUT, сервер выдает ответ об ошибке, в противном случае - ответ OK и URL-адрес для просмотра завершенной транзакции.

NB: в / account / john, "john" должен действительно быть уникальным номером счета Джона.

Автор: Tuckster Размещён: 03.04.2011 11:56

19 плюса

6801 Репутация автора

Большой вопрос, REST в основном объясняется примерами, подобными базам данных, где что-то хранится, обновляется, извлекается, удаляется Подобных примеров несколько, где сервер должен каким-то образом обрабатывать данные. Я не думаю, что Рой Филдинг включил что-либо в свой тезис, который в конце концов был основан на http.

Но он говорит о «передаче состояния представительства» как о конечном автомате со связями, переходящими в следующее состояние. Таким образом, документы (представления) отслеживают состояние клиента, а не сервер, который должен это делать. Таким образом, не существует состояния клиента, только указывается, по какой ссылке вы находитесь.

Я думал об этом, и мне кажется разумным, чтобы сервер обрабатывал что-то для вас, когда вы загружаете, сервер автоматически создает связанные ресурсы и дает вам ссылки на них (на самом деле это не Не нужно автоматически создавать их: он может просто сказать вам ссылки, и он будет создавать их только тогда, когда вы последуете за ними - ленивое создание). А также дать вам ссылки для создания новых связанных ресурсов - связанный ресурс имеет тот же URI, но длиннее (добавляет суффикс). Например:

  1. Вы загружаете ( POST ) представление концепции транзакции со всей информацией. Это похоже на вызов RPC, но на самом деле создает «предложенный ресурс транзакции». например, URI: /transaction глюки будут вызывать создание нескольких таких ресурсов, каждый с разным URI.
  2. В ответе сервера указывается URI созданного ресурса, его представление - это включает ссылку ( URI ) для создания связанного ресурса нового « ресурса подтвержденной транзакции». Другие связанные ресурсы - это ссылка для удаления предложенной транзакции. Это состояния конечного автомата, за которыми может следить клиент. Логически они являются частью ресурса, который был создан на сервере, помимо информации, предоставленной клиентом. например , идентификаторы URI: /transaction/1234/proposed, /transaction/1234/committed
  3. Вы отправляете POST на ссылку, чтобы создать «ресурс совершенной транзакции» , который создает этот ресурс, изменяя состояние сервера (баланс двух учетных записей) **. По своей природе этот ресурс может быть создан только один раз и не может быть обновлен. Поэтому глюки, совершающие много транзакций, не могут возникнуть.
  4. Вы можете получить эти два ресурса, чтобы увидеть, каково их состояние. Предполагая, что POST может изменить другие ресурсы, предложение теперь будет помечено как «зафиксированное» (или, возможно, вообще недоступное).

Это похоже на то, как работают веб-страницы: на последней странице написано «Вы уверены, что хотите это сделать?» Эта конечная веб-страница сама является представлением состояния транзакции, которое включает в себя ссылку для перехода в следующее состояние. Не только финансовые операции; также (например) предварительный просмотр, а затем зафиксировать в Википедии. Я предполагаю, что различие в REST состоит в том, что у каждой стадии в последовательности состояний есть явное имя (его URI).

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

OTOH Это похоже на игру с семантикой для меня; Меня не устраивает номинализация преобразования глаголов в существительные, чтобы сделать его RESTful, «потому что он использует существительные (URI) вместо глаголов (вызовы RPC)». т. е. существительное «зафиксированный ресурс транзакции» вместо глагола «совершить эту транзакцию». Я предполагаю, что одним из преимуществ номинализации является то, что вы можете ссылаться на ресурс по имени, вместо того, чтобы указывать его каким-либо другим способом (например, поддерживать состояние сеанса, чтобы вы знали, что такое «эта» транзакция ...)

Но важный вопрос: каковы преимущества этого подхода? т.е. чем этот стиль REST лучше, чем стиль RPC? Является ли метод, который отлично подходит для веб-страниц, также полезен для обработки информации, помимо хранения / извлечения / обновления / удаления? Я думаю, что ключевым преимуществом REST является масштабируемость; один из аспектов этого заключается в том, что нет необходимости явно поддерживать состояние клиента (но сделать его неявным в URI ресурса, а в следующих состояниях указывать ссылки в его представлении). В этом смысле это помогает. Возможно, это также помогает в наслоении / конвейерной обработке? OTOH только один пользователь будет смотреть на свою конкретную транзакцию, поэтому нет никакого преимущества в том, чтобы кэшировать ее, чтобы другие могли ее прочитать - большой выигрыш для http.

Автор: 13ren Размещён: 14.12.2011 04:33

1 плюс

2007 Репутация автора

Вы не должны использовать транзакции на стороне сервера в REST.

Один из недостатков ОТДЫХА:

Stateless

Связь клиент-сервер дополнительно ограничена тем, что между запросами не сохраняется клиентский контекст на сервере. Каждый запрос от любого клиента содержит всю информацию, необходимую для обслуживания запроса, и любое состояние сеанса сохраняется в клиенте.

Единственный способ RESTful - создать журнал повторных транзакций и перевести его в состояние клиента. С запросами клиент отправляет журнал повторов, а сервер повторяет транзакцию и

  1. откатывает транзакцию назад, но предоставляет новый журнал повторных транзакций (еще один шаг)
  2. или, наконец, завершить транзакцию.

Но, возможно, проще использовать технологию, основанную на сеансах сервера, которая поддерживает транзакции на стороне сервера.

Автор: bebbo Размещён: 05.07.2013 08:58

11 плюса

111 Репутация автора

Если вы отступите, чтобы подвести итоги обсуждения, вполне очевидно, что REST не подходит для многих API, особенно когда клиент-серверное взаимодействие является неотъемлемым состоянием, как это происходит с нетривиальными транзакциями. Зачем прыгать через все предложенные обручи, как для клиента, так и для сервера, чтобы педантично следовать некоторому принципу, который не подходит к проблеме? Лучший принцип - предоставить клиенту самый простой, естественный и продуктивный способ составить заявку.

Таким образом, если вы действительно выполняете много транзакций (типов, а не экземпляров) в своем приложении, вам действительно не следует создавать RESTful API.

Автор: Peris Размещён: 09.03.2014 03:57

1 плюс

13 Репутация автора

Я полагаю, что это был бы случай использования уникального идентификатора, сгенерированного на клиенте, чтобы гарантировать, что сбой соединения не подразумевает двуличность, сохраненную API.

Я думаю, что использование сгенерированного клиентом поля GUID вместе с объектом переноса и обеспечение того, чтобы тот же GUID не был повторно вставлен снова, было бы более простым решением вопроса банковского перевода.

Не знаю о более сложных сценариях, таких как бронирование нескольких авиабилетов или микро-архитектуры.

Я нашел статью на эту тему, касающуюся опыта работы с атомарностью транзакций в сервисах RESTful .

Автор: Eduardo Rolim Размещён: 26.03.2015 07:22

3 плюса

3129 Репутация автора

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

POST: accounts/alice, new Transfer {target:"BOB", abmount:100, currency:"CHF"}.

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


Но для редких случаев здесь общее решение:

Если вы хотите сделать что-то очень сложное, включающее много ресурсов в определенном контексте с множеством ограничений, которые фактически пересекают барьер «что против того, почему» (знание бизнеса и внедрения), вам нужно перенести состояние. Поскольку REST не должен содержать состояния, вам, как клиенту, необходимо перенести состояние в другое.

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

Таким образом, можно передавать внутреннее состояние, используя шифрование и подписывание системы, и при этом быть в целости и сохранности. Поиск подходящей абстракции для клиента, почему он передает информацию о состоянии, зависит от дизайна и архитектуры.


Реальное решение:

Помните, что REST говорит о HTTP, а HTTP идет с концепцией использования куки. Эти куки часто забываются, когда люди говорят о REST API, рабочих процессах и взаимодействиях, охватывающих несколько ресурсов или запросов.

Помните, что написано в Википедии о HTTP-куки:

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

В общем, если вам нужно передать состояние, используйте куки. Он разработан по той же самой причине, это HTTP и поэтому он совместим с REST по своему дизайну :).


Лучшее решение:

Если вы говорите о клиенте, выполняющем рабочий процесс с несколькими запросами, вы обычно говорите о протоколе. Каждая форма протокола поставляется с набором предварительных условий для каждого потенциального шага, например, выполнить шаг A, прежде чем вы сможете выполнить B.

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

Используя метафору Агента, вы можете предоставить ресурс, который может выполнить все необходимые для вас действия, и сохранить фактическое назначение / инструкции, на которые он действует, в своем списке (поэтому мы можем использовать POST для агента или «агентства»).

Сложный пример:

Покупка дома:

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

Эти шаги могут занять несколько дней, некоторые могут быть выполнены параллельно и т. Д.

Для этого вы просто даете агенту задание купить дом, например:

POST: agency.com/ { task: "buy house", target:"link:toHouse", credibilities:"IamMe"}.

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

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

Автор: Martin Kersten Размещён: 19.10.2015 09:22

10 плюса

13515 Репутация автора

Я отошел от этой темы на 10 лет. Возвращаясь, я не могу поверить, что религия маскируется под науку, в которую вы вступаете, когда вы гуглите отдых + надежный. Путаница мифическая.

Я бы разделил этот широкий вопрос на три части:

  • Нисходящие услуги. Любой веб-сервис, который вы разрабатываете, будет иметь нисходящие сервисы, которые вы используете, и синтаксис транзакций которых у вас нет другого выбора, кроме как следовать. Вы должны попытаться скрыть все это от пользователей вашего сервиса, и убедиться, что все части вашей работы успешно или неудачно в группе, а затем вернуть этот результат своим пользователям.
  • Ваши услуги. Клиенты хотят получить однозначные результаты при вызовах веб-сервисов, и обычный шаблон REST для выполнения запросов POST, PUT или DELETE непосредственно на предметных ресурсах кажется мне плохим и легко улучшаемым способом обеспечения этой уверенности. Если вы заботитесь о надежности, вам необходимо определить запросы действий. Этот идентификатор может быть guid, созданным на клиенте, или начальным значением из реляционной БД на сервере, это не имеет значения. Для сгенерированных сервером идентификаторов используйте запрос-ответ preflight для обмена идентификатором действия. Если этот запрос не выполнен или половина успешно выполнена, проблем нет, клиент просто повторяет запрос. Неиспользованные идентификаторы не причиняют вреда.

    Это важно, потому что позволяет всем последующим запросам быть полностью идемпотентными, в том смысле, что если они повторяются n раз, они возвращают один и тот же результат и больше ничего не вызывают. Сервер сохраняет все ответы против идентификатора действия, и если он видит тот же запрос, он воспроизводит тот же ответ. Более полное описание шаблона в этом документе Google . Документ предлагает реализацию, которая, я считаю (!), В целом следует принципам REST. Эксперты наверняка скажут мне, как это нарушает других. Этот шаблон может быть полезен для любого небезопасного вызова вашего веб-сервиса, независимо от того, вовлечены ли в него нисходящие транзакции.
  • Интеграция вашего сервиса в «транзакции», контролируемые вышестоящими сервисами. В контексте веб-сервисов полные ACID-транзакции обычно не стоят усилий, но вы можете значительно помочь потребителям вашего сервиса, предоставив ссылки отмены и / или подтверждения в вашем ответе на подтверждение, и, таким образом, добиться транзакций путем компенсации .

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

Автор: bbsimonbb Размещён: 15.02.2016 09:35
32x32