在实践中用Scala演员编写应用程序II

时间:2009-08-21 09:34:22

标签: scala actor

因为我的第一个问题太长了,所以我要问这是一个单独的问题。这是另一个关于基于actor的应用程序的体系结构。

通过应用程序跟踪邮件路径

我们来看一段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中。

使用Actor建模并用4个独立的演员替换我的4个组件,如何跟踪(在我看来)过程涉及的内容?特别是如果我避免使用 !? 运算符,那么我可能会向我的ConditionalDelete发送消息PermissionActor,这将发送向GetTradesAndDelete发送消息PersistenceActor,然后发送更多消息等。处理删除的代码将散布在我的应用程序中。

这也意味着几乎每个演员都需要处理其他演员(为了转发消息)。

与我之前的问题一样,人们如何处理这个问题?有没有一个很好的建模工具,可以让你跟踪所有这些?人们使用 !? 我是否将过多的组件转换为Actor

2 个答案:

答案 0 :(得分:6)

你肯定使用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)
}

这里我们使用currying在第一个回复的答案中传递“日期”。如果有很多与交互相关的参数,那么最好将所有正在进行的事务的信息保存在本地HashSet中,并且只传递一个令牌,用于在收到答案时找到该信息。

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

修改

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

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) _)

关于cur and和功能的一些解释。课程DeleteTradesPermission是curry,以便您可以在其上传递Date,并使用PermissionSet完成其他功能。这将是答案信息的模式。

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

如果没有预期答案,例如DeleteTradesSendCancelReportsUpdateWithDeletedTrades用于本示例的目的,那么您不需要传递函数回复消息。

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

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

答案 1 :(得分:1)

如果不考虑,不应将演员用于替换传统的服务组件。

我们今天通过培训编写的大多数服务组件都是无状态的。无状态服务组件比actor更容易管理(没有消息类等)。与actor相比,他们缺少的一件事是异步执行。但是当客户希望结果在大多数时间内同步返回时,同步无状态服务组件就可以胜任这项工作。

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

演员应该警惕处理消息时所做的任何阻止处理。由于actor按顺序处理它们的消息,因此只要它们在“act()”内等待I / O被阻止,它们就无法处理其邮箱中的任何其他消息。这里使用的Scala习惯用法是启动另一个执行实际阻塞操作的ad-hoc actor。这种反模式更加影响基于事件的(反应)角色,因为它阻止了基于事件的actor被捎带的线程。

从我可以收集的信息来看,您对服务组件的所有四次调用都可能是阻塞的,因此在将它们转换为actor时应该注意使它们扩展。