【问题标题】:Akka actors always times out waiting for futureAkka 演员总是超时等待未来
【发布时间】:2016-03-25 12:17:02
【问题描述】:

我有下面定义的以下参与者,用于“登录”用户。

object AuthenticationActor {
  def props = Props[AuthenticationActor]

  case class LoginUser(id: UUID)
}

class AuthenticationActor @Inject()(cache: CacheApi, userService: UserService) extends Actor{
  import AuthenticationActor._

  def receive = {
    case LoginEmployee(id: UUID) => {
      userService.getUserById(id).foreach {
        case Some(e) => {
          println("Logged user in")
          val sessionId = UUID.randomUUID()
          cache.set(sessionId.toString, e)
          sender() ! Some(e, sessionId)
        }
        case None => println("No user was found")
      }
    }
  }
}

注意:userService.getUserById 返回 Future[Option[User]]

下面是对它的非常简单的 API cal

class EmployeeController @Inject()(@Named("authentication-actor") authActor: ActorRef)(implicit ec: ExecutionContext) extends Controller {

  override implicit val timeout: Timeout = 5.seconds

  def login(id: UUID) = Action.async { implicit request =>
    (authActor ? LoginUser(id)).mapTo[Option[(User, UUID)]].map {
      case Some(authInfo) =>   Ok("Authenticated").withSession(request.session + ("auth" -> authInfo._2.toString))
      case None => Forbidden("Not Authenticated")
    }
  }
}

两个println 调用都会执行,但login 调用总是会失败,说请求超时。有什么建议吗?

【问题讨论】:

  • 你不应该使用 pipeTo i/o mapTo 吗?

标签: scala akka actor


【解决方案1】:

当您执行此类操作(在Futures 回调中访问发件人)时,您需要在收到请求时将sender 存储在外部范围内的val 中,因为它很可能在Future 完成之前发生更改。

def receive = {
    case LoginEmployee(id: UUID) => {
      val recipient = sender

      userService.getUserById(id).foreach {
        case Some(e) => {
          ...
          recipient ! Some(e, sessionId)
        }
        ...
      }
    }
  }

当找不到用户时,您也永远不会发送结果。

你实际上应该做的是将Future结果传递给sender

def receive = {
  case LoginEmployee(id: UUID) => {
    userService.getUserById(id) map { _.map { e =>
        val sessionId = UUID.randomUUID()
        cache.set(sessionId.toString, e)
        (e, sessionId)
      }
    } pipeTo sender
  }
}

或带有指纹

def receive = {
  case LoginEmployee(id: UUID) => {
    userService.getUserById(id) map { 
      case Some(e) =>
        println("logged user in")
        val sessionId = UUID.randomUUID()
        cache.set(sessionId.toString, e)
        Some(e, sessionId)
      case None =>
        println("user not found")
        None
    } pipeTo sender
  }
}

【讨论】:

  • 在你的第一个例子中,我认为val recipient = sender 应该是val recipient = sender()(注意括号)。
  • Scala 允许省略 0-arity 方法的括号,请参阅:docs.scala-lang.org/style/method-invocation.html
  • 我很抱歉 - 我可以发誓我只是在某个地方读到这样做是不好的做法(甚至是不正确的),但我再也找不到参考了。一定是看错了。
猜你喜欢
  • 2014-11-26
  • 2019-01-31
  • 2017-05-07
  • 2019-10-28
  • 2021-05-13
  • 2012-11-28
  • 1970-01-01
  • 2020-05-19
  • 1970-01-01
相关资源
最近更新 更多