Условная синхронизация нескольких запросов приложений - PullRequest
1 голос
/ 29 декабря 2011

У меня следующая проблема:

У меня есть многопоточное приложение на стороне сервера, где каждый запрос выполняет новый поток (стандартный случай). На основании этого запроса приложение проверяет, кэшируются ли необходимые данные уже в БД. Если да, он выбирает его и отправляет обратно клиенту. Если нет, то удаленная служба называется ответом, который сохраняется в БД и затем выбирается для возврата клиенту.

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

Какое решение выбрать? Одна идея заключалась бы в том, чтобы поместить конкретные ограничения поля UNIQUE в БД. Таким образом, даже если приложение попытается написать дважды, БД просто отклонит его, и приложение сгенерирует исключение. Однако служба все равно будет выполняться несколько раз.

Другое решение, которое пришло мне в голову, - это синхронизировать метод, который вызывает веб-сервис. Таким образом, каждый последующий запрос к моему приложению будет помещен в стек ожидания до завершения предыдущей операции. Таким образом, если поступит другой запрос с теми же входными данными, в то время как первый все еще ожидает результатов от удаленного сервиса, он будет сидеть и ждать завершения операции. Затем, когда второй запрос проверяет БД, данные уже будут там, и больше не будет необходимости вызывать сервис. Это также предотвратит наличие нескольких одинаковых записей в БД. Проблема в том, что скорость моего серверного приложения неизбежно будет снижаться, потому что КАЖДЫЙ запрос придется ждать, даже те, которые обычно не должны (те, для которых, предположительно, у нас уже есть данные в БД)

Есть еще предложения? Я застрял. Как я могу реализовать какую-то условную синхронизацию?

Ответы [ 4 ]

2 голосов
/ 29 декабря 2011

Просто реализуйте этот алгоритм:

  • получить данные из БД
  • если данные существуют, вернуть данные
  • если данных не существует, получить блокировку, связанную с идентификатором данных для получения // блокировки вызова
  • когда у вас есть блокировка, проверьте, есть ли данные в БД, и если да, верните данные. Иначе, позвоните в службу и положите данные в БД
  • снять блокировку

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

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

1 голос
/ 29 декабря 2011

Вы можете просто синхронизировать расчетную часть, то есть использовать двойную проверку блокировки:

boolean dataExists = checkDB();
if( !dataExists ) {
  synchronized( someSharedObject ) {
    //another thread might have been waiting too and now has put the data into the DB
    //thus check again
    if( !checkDB() ) {
      calculateDataAndWriteToDB();
    }  
  }
}
//do whatever you want with the existing data
0 голосов
/ 29 декабря 2011

При первом подходе с УНИКАЛЬНЫМИ ограничениями полей в БД ваша проблема не решится, так как endUser придется ждать возврата удаленного вызова службы, а ресурсы будут использоваться ненужно.

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

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

0 голосов
/ 29 декабря 2011

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

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

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