23

我正在开发一个用 Scala 编写的 Web 应用程序,使用 Play!框架和 Akka。代码基本上是这样组织的:播放控制器向 Akka 演员发送消息。参与者依次与抽象数据库访问的持久层对话。在应用程序中使用这些组件的典型示例:

class OrderController(orderActor: ActorRef) extends Controller {
  def showOrders(customerId: Long) = {
    implicit request => Async {
      val futureOrders = orderActor ? FindOrdersByCustomerId(id)

      // Handle the result, showing the orders list to the user or showing an error message.
    }
  }
}

object OrderActor extends Actor {
  def receive = {
    case FindOrdersByCustomerId(id) => 
      sender ! OrderRepository.findByCustomerId(id)
    case InsertOrder(order) =>
      sender ! OrderRepository.insert(order)
      //Trigger some notification, like sending an email. Maybe calling another actor.
  }
}

object OrderRepository {
  def findByCustomerId(id: Long): Try[List[Order]] = ???
  def insert(order: Order): Try[Long] = ???
}

如您所见,这是基本的 CRUD 模式,与您在其他语言和框架中看到的非常相似。查询被传递到下面的层,当应用程序从数据库中获取结果时,该结果会返回,直到它到达 UI。唯一相关的区别是参与者和异步调用的使用。

现在,我对演员的概念很陌生,所以我还不太明白。但是,根据我所读到的,这不是演员应该被使用的方式。但是请注意,在某些情况下(例如,在插入订单时发送电子邮件)我们确实需要真正的异步消息传递。

所以,我的问题是:以这种方式使用演员是个好主意吗?利用 Futures 和 Akka 的其他并发功能,在 Scala 中编写 CRUD 应用程序的替代方案是什么?

4

2 回答 2

5

虽然基于actor的并发不适合开箱即用的事务操作,但是如果你很好地使用持久层,这并不能阻止你以这种方式使用actor。如果您可以保证插入( write )是原子的,那么您可以安全地让一个演员池为您做这件事。通常数据库具有线程安全读取,因此find也应该按预期工作。除此之外,如果插入不是线程安全的,您可以拥有一个专门用于写入操作的 WriteActor,并且消息的顺序处理将确保您的原子性。

于 2012-12-19T13:41:57.593 回答
2

需要注意的一件事是参与者一次处理一条消息,在这种情况下这将是相当有限的。您可以通过使用路由器来使用参与者池。

您的示例定义了存储库的阻塞 api,这可能是您唯一可以做的事情,具体取决于您的数据库驱动程序。如果可能的话,您也应该在那里使用异步 api,即返回 Futures。然后,在 actor 中,您将通过管道将 Future 的结果发送给发送者。

于 2012-12-19T14:24:56.493 回答