Состояние возврата сервера 200, но клиент не получает его из-за разрыва сетевого подключения - PullRequest
0 голосов
/ 24 мая 2018

У меня есть служба REST и клиент (приложение для Android), которые отправляют запрос POST службе REST.На стороне клиента есть документы (заказы), которые необходимо синхронизировать с веб-сервером.Синхронизация означает, что клиент отправляет запрос POST службе REST для каждого заказа.Когда служба REST получает запрос POST, она записывает данные в базу данных и отправляет ответ со статусом 200 клиенту.Клиент получает 200 и помечает этот заказ как синхронизированный.

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

Что такое хорошая практика для решения проблемы такого рода?

Ответы [ 4 ]

0 голосов
/ 24 мая 2018

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

Добро пожаловать в мир ненадежных сообщений.

Что такоехорошая практика для решения такого рода проблем?

Вам следует ознакомиться с «Никому не нужен надежный обмен сообщениями» от Марк де Грау (2010).

Краеугольный каменьнадежный обмен сообщениями - это идемпотентная обработка запросов.Идемпотент семантика описывается таким образом

Метод запроса считается «идемпотентом», если предполагаемый эффект на сервере нескольких идентичных запросов с помощью этого метода такой же, как эффект дляодин такой запрос.

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

Существует два основных шаблона для обработки идемпотентных запросов.Более простым из них является set, что означает «перезаписать текущее представление тем, которое я предоставляю».

// X == 6

server.setX(7)
// X == 7

server.setX(7) <- a second, identical request, but the _effect_ is the same.
// X == 7

Альтернатива - test and set (иногда называемая compare and swap);в этом шаблоне запрос состоит из двух частей: предикат, который определяет, выполняется ли какое-либо условие, и изменение, которое применяется, если условие выполняется.

// X == 6

server.testAndSetX(6,7)
// X == 7 

server.testAndSetX(6,7) <- this is a no op, because 7 != 6
// X == 7

Это основная идея.

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

// collection.get(Id.of(7)) == Nothing

collection.put(Id.of(7), 7)
// collection.get(Id.of(7)) == Just(7)

collection.put(Id.of(7), 7) <- a second, identical request, but the _effect_ is the same.
// collection.get(Id.of(7)) == Just(7)

Если это не вариант, тогда вам нужно немногосвойство коллекции, которое изменится, когда ваше редактирование будет выполнено, закодировано в запросе

if (collection.size() == 3) {
   collection.append(7)
}

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

// begin transaction
if (resource.version.get() == expectedVersion) {
   resource.version.set(1 + expectedVersion)
   resource.applyChange(request)
}
// end transaction

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

Во всех этих test and set сценариях мы описываем понятие условного запроса

Условными запросами являются HTTP-запросы [RFC7231], которые включают одно или несколько полей заголовка, указывающих предварительное условие, которое необходимо проверить перед применением семантики метода кцелевой ресурс.

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

Обратите внимание: то, что это работает, не дает нам гарантии того, что сервер будет делать то, что хочет клиент.Вместо этого он слабее: клиент может безопасно повторять запрос, пока не получит подтверждение от сервера.

0 голосов
/ 24 мая 2018

Одним из возможных вариантов будет проверка на стороне сервера.Порядок должен иметь какой-то параметр уникальности: имя или идентификатор или что-то еще.Но этот параметр также должен быть отправлен клиентом.Затем вы получите это значение (например, если имя уникально и клиент отправит его), найдите этот порядок в базе данных.Если заказ найден, вам не нужно сохранять его в базе данных, и вы должны отправить клиенту 409 Conflict response.Если вы не нашли такой заказ в базе данных, то сохраните его и отправите ответ 201 Ok.

Рекомендации:

  • 201 Ok для POST
  • 409 Conflict- если ресурс уже существует
0 голосов
/ 24 мая 2018

Ваши запросы должны быть idempotent .
Из вашего описания вы должны использовать PUT вместо POST.
Сгенерированные на стороне клиента идентификаторы (направляющие) и Upsert логикана стороне сервера, помогите достичь этого.
Таким образом, вы можете реализовать клиентскую сторону логики повторных попыток для неудачных запросов, не вводя несколько записей.

0 голосов
/ 24 мая 2018

Конечно, ваши документы должны иметь уникальный идентификатор.Семантически правильным способом было бы использовать заголовок If-None-Match, куда вы отправляете этот идентификатор.

Затем сервер проверяет, существует ли уже документ с таким идентификатором, и ответит 412 Precondition Failed, если этослучай.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...