代码之家  ›  专栏  ›  技术社区  ›  Adam Batkin

在多层体系结构中执行批量操作时处理错误和反馈

  •  11
  • Adam Batkin  · 技术社区  · 15 年前

    假设您有一个业务逻辑方法,可以跨多个对象执行某些操作。也许您想为从列表中选择的每个人调用一个彩票号码选择web服务。在Java中,代码可能如下所示:

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

    请注意,彩票号码web服务可能有副作用,如记录某人已请求彩票号码(可能会向其帐户收费),因此,即使一个人的web服务调用失败,其他人的web服务调用也可能成功。这些信息(彩票号码)需要反馈到更高的层次(视图)。

    如果这是一个单一操作发生的情况,那么业务逻辑方法可以返回一个值(比如彩票号码),或者抛出一个异常,其中包含失败的任何细节。但对于批量操作,有几个操作可能成功,有几个操作可能失败。

    这似乎是在许多应用程序中都会出现的一种问题,应该有一种干净的方法来处理它。那么,从业务逻辑层向应用程序中的另一层(如视图)反馈这类信息的最佳方式是什么呢?最好是以一种通用的方式,这种方式可以用于不同类型的数据和操作?

    8 回复  |  直到 15 年前
        1
  •  10
  •   DanO Roman    15 年前

    这个问题强调了异常处理、事务和想法的适当使用之间的重要区别 工作流“补偿” 这就是提问者在正确陈述以下内容时试图达到的目的:

    这似乎是在许多应用程序中都会出现的一种问题,应该有一种干净的方法来处理它。

    数据事务最初是按照复式记账法建模的——一种 以及相应的 借记

    补偿 进来。 Here's one introduction to those concepts

    例如,如果你从亚马逊订购一本书,他们可能不会在你的购物车中“锁定”该记录,甚至在确认订单时使用严格的交易来确定该书是否仍在库存中。不管怎样,他们都会把它卖给你,并在可能的时候把它运出去。如果他们在几周内还没有设法把它上市,他们可能会给你发一封电子邮件,告诉你他们正在努力满足你的需求,你可以继续等待他们把它上市,或者你可以取消订单。这称为补偿,在许多实际业务流程中都是必需的。

    最后,还有一个问题 没什么特别的 good rules for when to throw an exception )。您也不应该依赖特定于工具(WCF?)的机制来查看或处理服务实现中发生的异常。通信故障应该是数据契约(故障契约)的正常部分。

    总结:

    • 您的问题已经超出了事务的概念-->研究工作流程补偿。

    祝你好运-

        2
  •  1
  •   simon    15 年前

        3
  •  1
  •   Justin Cormack    15 年前

    我认为如果你用这些术语来思考的话,你真的过度使用了异常!

    返回表示失败的值而不是抛出异常是完全可以的。通常情况下更好。当您无法在抽象级别恢复时,最好使用异常,但不应将其用作控制流的主要手段,否则您的程序将很难阅读。

        4
  •  1
  •   Scott McKenzie    15 年前

    • 错误和受影响的域对象的列表。基本域对象或具有持久ID的对象可能有助于重用。例如,引用域对象的错误集合。
    • 您可以向Person对象中注入(AOP,DI)某种错误对象/消息。例如,如果(个人错误){…}
    • 您的所有域对象都可能包含一个可通过接口访问的错误集合;或支持IHasErrors接口的人员。您可以将其设置为通用的,并使用一个支持警告、验证和所有方式的基本错误对象。

    如果你有一些具体的问题,很乐意深入研究一下。

        5
  •  1
  •   Adam Batkin    15 年前

    理想情况下,对web服务的调用应该是这样的。

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

    为每个人打一个web服务调用是昂贵的。在服务器端,您需要迭代列表并执行操作。 服务器端代码可能引发2个异常: BulkOperationFailedException-如果由于数据库关闭或配置文件丢失而出现致命错误。无法进一步处理。 BulkOperationException-包含一系列与个人相关的异常。您可以保留一些id以唯一地引用每个对象。 您的代码如下所示:

    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
    

    当没有引发异常时,操作被视为成功。您可以为失败设置自定义错误代码,而不是使用用户定义的异常。 在服务器端构建BulkOperationException是一件棘手的事情。其次,您需要将服务器端抛出的错误分类为BulkOperationFailedException和BulkOperationException。在我的一个项目中,我就是这样处理的

        6
  •  0
  •   cwap    15 年前

    DTOs 对于这种任务。DTO还可以包括关于持久化是否成功的信息以及其他类型的“元数据”。

        7
  •  0
  •   pjp    15 年前

    我可能会返回类型为的结果映射 Map<Person,Future<String>> getLotteryNumbers<Collection<Person>> 服务

    然后,我将遍历地图并使用 Future.get() 获取彩票号码或引发的异常。

    在我的一些服务中,我喜欢将所有调用都实现为单个项调用,然后在我的服务中使用逻辑对它们进行批处理,并将它们作为一个组进行处理。批处理是使用 LinkedBlockingQueue 和一个轮询线程。

    在这个场景中,我返回一个 Future<Thing> CountdownLatch .

    http://jcip.net/

        8
  •  0
  •   Rad    15 年前

    另一种方法,特别是对于高吞吐量系统,将使用基于队列的设计,其中处理实体将对对象执行操作,然后根据结果将对象放入不同的队列中,以便其他实体进行额外处理,然后继续。这将减少因额外处理而产生的瓶颈,例如处理缺货产品的订单

    推荐文章