因为我的第一个问题太长了,所以我要问这是一个单独的问题。这是另一个关于基于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
?
答案 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
。
如果没有预期答案,例如DeleteTrades
,SendCancelReports
和UpdateWithDeletedTrades
用于本示例的目的,那么您不需要传递函数回复消息。
由于我们期望一个函数返回一个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时应该注意使它们扩展。