【问题标题】:Writing applications with Scala actors in practice II [closed]在实践 II 中使用 Scala 演员编写应用程序 [关闭]
【发布时间】:2010-11-21 14:38:44
【问题描述】:

因为我的第一个问题太长了,所以我把它作为一个单独的问题来问。这是关于基于参与者的应用程序架构的另一篇文章。

通过应用程序跟踪消息路径

我们来看一段Java代码:

public void deleteTrades(User user, Date date) {
    PermissionSet ps = permissionService.findPermissions(user)
    if (ps.hasPermission("delete")) {
        Set<Trade> ts = peristence.findTrades(date);
        reportService.sendCancelReports(ts);
        positionService.updateWithDeletedTrades(ts);
    }
}

在这段代码中,我有 4 个独立的组件,并且过程 deleteTrades 所需的它们之间的交互是明确定义的。它完全包含在方法deleteTrades中。

使用Actors 对此进行建模并用 4 个单独的参与者替换我的 4 个组件,我如何(在我的脑海中)跟踪程序涉及的内容?特别是如果我避免使用 !? 运算符,那么我很可能会向我的PermissionActor 发送消息ConditionalDelete,这将向我的GetTradesAndDelete 发送消息我的PersistenceActor 然后将发送更多消息等。处理删除的代码将散布在我的应用程序中。

这也意味着几乎每个参与者都需要一个其他参与者的句柄(以便转发消息)。

在我之前的问题中,人们如何处理这个问题?是否有一个好的建模工具可以让您跟踪所有这些?人们是否使用 !? 我是否将太多组件转换为 Actors?

【问题讨论】:

    标签: scala actor


    【解决方案1】:

    您肯定使用了 5 个组件。有处理特定任务的演员,还有一个协调者。

    当然,您必须有的问题是如何异步链接它。嗯,它实际上有点简单,但它可能会使代码变得模糊。基本上,您向每个组件发送您想要的回复。

    react {
      case DeleteTrades(user,dates) => 
        PermissionService ! FindPermissions(user, DeleteTradesPermissions(dates) _)
      case DeleteTradesPermissions(dates)(ps) =>
        if (ps hasPermission "delete")
          Persistence ! FindTrades(date, DeleteTradesTradeSet _)
      case DeleteTradesTradeSet(ts) =>
        ReportService ! SendCancelReports(ts)
        PositionService ! UpdateWithDeletedTrades(ts)
    }
    

    这里我们使用柯里化在第一个返回的答案中传递“日期”。如果有很多与交互相关的参数,最好将所有正在进行的事务的信息保存在本地 HashSet 中,并传递一个令牌,您将在收到答案时使用该令牌来定位该信息。

    请注意,这个单一参与者可以处理多个并发操作。在这种特殊情况下,只需删除事务,但您可以添加任意数量的不同操作来处理。当一项操作所需的数据准备就绪时,该操作将继续。

    编辑

    以下是如何定义这些类的工作示例:

    class Date
    class User
    class PermissionSet
    
    abstract class Message
    case class DeleteTradesPermission(date: Date)(ps: PermissionSet) extends Message
    case class FindPermissions(u: User, r: (PermissionSet) => Message) extends Message
    
    FindPermissions(new User, DeleteTradesPermission(new Date) _)
    

    关于柯里化和函数的一些解释。 DeleteTradesPermission 类是 curried 的,因此您可以在其上传递 Date,并让其他函数使用 PermissionSet 完成它。这将是应答消息的模式。

    现在,FindPermissions 类接收一个函数作为第二个参数。接收到这个消息的actor会将返回值传递给这个函数,并会收到一个Message作为答案发送。在此示例中,消息将同时包含调用方发送的 Date 和应答方提供的 PermissionSet

    如果没有预期的答案,例如DeleteTradesSendCancelReportsUpdateWithDeletedTrades 在本示例中的情况,则不需要传递返回消息的函数。

    由于我们期望一个函数返回一个 Message 作为那些需要答案的消息的参数,我们可以这样定义特征:

    trait MessageResponse1[-T1] extends Function1[T1, Message]
    trait MessageResponse2[-T1, -T2] extends Function2[T1, T2, Message]
    ...
    

    【讨论】:

    • Daniel - 你能告诉我DeleteTradesPermissions 的类定义吗?这是一个案例课(我不知道你可以咖喱他们)?您是否定义了自己的提取器?如果是,如何定义?
    • 好吧,这段代码真的不起作用,你知道的。这是一个例子。我看到缺少“”来强制部分应用——我认为柯里化不需要。但我很确定它_可以完成,只需一点点工作。在这里,我解决了一些问题,我会发布它。
    【解决方案2】:

    Actor 不应该被用来替换传统的服务组件而不加考虑。

    我们现在通过训练编写的大多数服务组件都是无状态的。无状态服务组件比参与者更容易管理(无消息类等)。但是,与演员相比,他们缺乏的一件事是异步执行。但是当客户端大部分时间都期望结果同步返回时,同步无状态服务组件就可以胜任了。

    当需要管理内部状态时,Actor 非常适合。无需在“act()”内部进行任何同步来访问内部状态并担心竞争条件。只要 ”!?”没有在“act()”中使用,死锁也应该被最小化。

    Actor 在处理消息时应该警惕任何阻塞处理。由于actor按顺序处理他们的消息,任何时候他们被阻塞在“act()”中等待I/O,他们就不能处理他们邮箱中的任何其他消息。这里使用的 Scala 习惯用法是启动另一个执行实际阻塞操作的 ad-hoc actor。这种反模式对基于事件(反应)的actor的影响更大,因为它也阻塞了基于事件的actor所搭载的线程。

    据我所知,您对服务组件的所有四个调用都可能会阻塞,因此在将它们转换为演员时应注意使其可扩展。

    【讨论】:

    • @walter;我不确定你是否回答了我的问题。我非常了解 Actor 的工作方式,我试图描述将典型 Java 代码转换为异步和非阻塞 Actor 代码的问题:即当程序逻辑分布在我的应用程序中时,我如何跟踪它?跨度>
    猜你喜欢
    • 1970-01-01
    • 2020-10-08
    • 1970-01-01
    • 1970-01-01
    • 2017-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    相关资源
    最近更新 更多