Актеры Akka всегда в ожидании будущего

У меня есть следующий актер, как определено ниже, предназначенный для "входа" пользователя.

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, вызванный этим

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 Вызов всегда потерпит неудачу, сказав, что время запроса истекло. Какие-либо предложения?

1 ответ

Решение

Когда вы делаете это (доступ к отправителю в Futures callback) нужно хранить 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
  }
}
Другие вопросы по тегам