4

Because my first question was so long, I'm asking this as a separate question. It's another one about the architecture of an actor-based application.

Keeping track of message paths through an Application

Let's take a piece of Java code:

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);
    }
}

In this code I have 4 separate components and the interaction between them required for the procedure deleteTrades is well-defined. It's completely contained in the method deleteTrades.

Modelling this with Actors and replacing my 4 components with 4 separate actors, how do I keep track (in my mind) of what a procedure involves? Particularly if I'm avoiding using the !? operator, then it's likely that I'll be sending a message ConditionalDelete to my PermissionActor, which will be sending a message GetTradesAndDelete to my PersistenceActor which will then send further messages etc etc. The code to process a delete will be strewn across my application.

It also means that pretty much every actor needs a handle on every other actor (in order to forward messages).

As in my previous question, how do people deal with this? Is there a good modelling tool which lets you keep track of all this? Do people use !? Am I turning too many components into Actors?

4

2 回答 2

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

在这里,我们使用柯里化在第一个返回答案中传递“日期”。如果有很多与交互相关的参数,最好将所有正在进行的事务的信息保存在本地 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 的,因此您可以传递 a Date,并让其他一些函数用PermissionSet. 这将是应答消息的模式。

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

如果没有预期的答案,例如 的情况DeleteTradesSendCancelReports并且UpdateWithDeletedTrades出于本示例的目的,那么您不需要传递返回消息的函数。

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

trait MessageResponse1[-T1] extends Function1[T1, Message]
trait MessageResponse2[-T1, -T2] extends Function2[T1, T2, Message]
...
于 2009-08-21T17:49:54.413 回答
1

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

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

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

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

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

于 2009-08-22T03:37:29.513 回答