Akka:将响应发回给 Actor

Akka: send back response to Actor

我有以下代码向发送方 Actor 发回响应(响应 actor 使用 Slick 从 table 加载列表):

class ManageUsersData extends Actor {

  def receive = {
    case _       => {
      sender ! loadUsers
    }
  }


  def loadUsers =  {

    var list = new ListBuffer[String]()
    val db = Database.forConfig("dbconfig")
    try {

      val users: TableQuery[Users] = TableQuery[Users]
      val future = db.run(users.result)


      future onComplete {
        case Success(u) => u.foreach {
          user => {
            list += user.firstName
          }
          list
        }
        case Failure(t) => println("An error has occured: " + t.getMessage)
      }

    } finally db.close

   list

  }
}

这里的问题是 loadUsers returns 在等待 Future 完成之前。如何解决这个问题?

你应该使用 pipe pattern:

import akka.pattern.pipe

// ...

def receive = {
  val originalSender = sender
  loadUsers pipeTo originalSender
}

Jean Logeart 已经提到了概念性解决方案。关于 loadUsers,我认为这是一个较短的版本?

def loadUsers =  {

    val db = Database.forConfig("dbconfig")
    try {

      val users: TableQuery[Users] = TableQuery[Users]
      db.run(users.result).map(_.firstName)

    } catch {
      case e: Exception => println("An error has occured: " + e.getMessage)
    } finally db.close

 }

如我所见,最简单的方法是简单地将 future 发送回 sender,而不是 async-filled list.

  def loadUsers =  {

    val db = Database.forConfig("dbconfig")
    try {

      val users: TableQuery[Users] = TableQuery[Users]
      
      val future = db.run(users.result)

      future.map {     //the future
        _.map {        //the returning Seq
          _.firstName 
        }
      }

    } finally db.close

  }

现在调用方 actor 有处理未来或失败的负担。

这也有缺点,如果 sender 使用 ask/? 操作,异步结果将是 Future 包装进一步的 Future.

您可以使用 pipeTo 方法解决这个问题,该方法将未来的消息发送给调用者,而无需费心解包。

piping 结果的缺点是 sender 应该有办法识别哪个回复属于哪个请求。一个可能的解决方案是发送一个请求标识符,该标识符将与答案一起发回,因此请求者可以轻松地 link 两者。

旁注

为什么要在未来的结果中映射 firstName 属性,而不是在 slick 查询中使用投影?我假设这是为了让示例保持简单。