Обработка ошибок и обратной связи при выполнении массовых операций в многоуровневой архитектуре - PullRequest
12 голосов
/ 27 августа 2009

Допустим, у вас есть метод бизнес-логики, который может выполнять некоторые операции с несколькими объектами. Может быть, вы хотите позвонить в веб-службу выбора номера лотереи, один раз для каждого человека, выбранного из списка. В Java код может выглядеть примерно так:

Set<Person> selectedPeople = ... // fetch list of people
for ( Person person : selectedPeople ) {
    String lotteryNumber = callLotteryNumberWebService( person );
    // ...
}

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

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

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

Ответы [ 8 ]

10 голосов
/ 01 сентября 2009

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

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

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

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

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

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

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

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

Резюме:

  • Ваша проблема переросла концепцию транзакции -> посмотрите на компенсацию рабочего процесса.

Удачи -

1 голос
/ 07 сентября 2009

В идеале звонок на ваш веб-сервис должен быть таким:

List<Person> selectedPeople = ... //fetch list of people
callLotteryNumberWebService(selectedPeople.ToArray );

Посылка вызова через Интернет для каждого человека является дорогостоящей. В конце сервера вам нужно перебрать список и выполнить операцию. Код на стороне сервера может выдавать 2 исключения: BulkOperationFailedException - если есть фатальная ошибка из-за нехватки базы данных или отсутствующего файла конфигурации. Дальнейшая обработка невозможна. BulkOperationException - это содержит массив исключений, касающихся человека. Вы можете сохранить некоторый идентификатор для уникальной ссылки на каждый объект. Ваш код будет выглядеть так:

List<Person> selectedPeople = ... // fetch list of people 

try{
    callLotteryNumberWebService(selectedPeople.ToArray);
}catch  (BulkOperationFailedException e) {
    SOP("Some config file missing/db down.No person records processed")
}catch(BulkOperationException e)  {
    UserDefinedExceptions us =  e.getExceptions()
    foreach(exception ein us)   {
        // read unique id to find which person object failed
    }
}

construct msg based on which personobject succeeded and which failed

Операция считается успешной, если не было выдано никаких исключений. Вы можете использовать собственные коды ошибок для сбоев вместо использования исключений UserDefined. Построить исключение BulkOperationException на стороне сервера сложно. Во-вторых, вам нужно классифицировать ошибки, выдаваемые со стороны сервера, в BulkOperationFailedException и BulkOperationException. Вот как я справился с одним из моих проектов

1 голос
/ 07 сентября 2009

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

  • Список ошибок и затронутых объектов домена. Объект базового домена или что-то с постоянным идентификатором может быть полезным для повторного использования. Например. набор ошибок, относящихся к объектам домена.
  • Вы можете внедрить (AOP, DI) в объект Person какой-либо объект / сообщение об ошибке. Например. if (персона. Ошибки) {...}
  • Вы можете заключить коллекцию Person в сообщение с заголовком, телом, информацией об ошибке
  • Все ваши доменные объекты могут включать коллекцию ошибок, доступную через интерфейс; или Person поддерживает интерфейс IHasErrors. Вы можете сделать это универсальным и использовать базовый объект Error, поддерживающий предупреждения и валидацию, а также всевозможные вещи.

Если вы работаете в подлинной многоуровневой (а не многоуровневой) системе, то у вас может быть архитектура на основе сообщений, которая может легко приспособиться к какому-то общему механизму ошибки / предупреждения / проверки. Системы SOA / Ajax поддаются этому.

Рад углубиться немного глубже, если у вас есть конкретные вопросы.

1 голос
/ 31 августа 2009

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

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

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

1 голос
/ 27 августа 2009

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

0 голосов
/ 02 сентября 2009

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

0 голосов
/ 31 августа 2009

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

0 голосов
/ 31 августа 2009

Я бы, вероятно, вернул карту результатов типа Map<Person,Future<String>> из моей службы getLotteryNumbers<Collection<Person>>.

Затем я бы перебрал карту и использовал Future.get(), чтобы получить либо номер лотереи, либо выброшенное исключение.

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

В этом сценарии я возвращаю Future<Thing>, который ожидает, пока результаты партии будут доступны, используя CountdownLatch.

Взгляните на Java Concurrency на практике, чтобы увидеть, как все эти компоненты могут работать вместе http://jcip.net/

...