Scala Akka Actors: как отправить результат ответа Http обратно отправителю?

Я пытаюсь выполнить следующий код Scala внутри Akka Actor.

class FilteringService(implicit timeout: Timeout) extends Actor {

  def receive: PartialFunction[Any, Unit] = {

    case GetProfiles ⇒
      val requester = sender
      def getProfiles = {

        var result = new Array[Profile](0)

        println("[GET-PROFILES] Entered, making request")
        val req = Get("http://localhost:9090/profiles")
        implicit val profileFormat = jsonFormat16(Profile)


        val responseFuture: Future[HttpResponse] = Http().singleRequest(req)
        println("[GET-PROFILES] Entered, request sent")
        responseFuture.onComplete {

          case Success(response) =>
            println("[RES - SUCCESS] Request returned with " + response.status)
            val responseAsProfiles =  Unmarshal(response.entity).to[Array[Profile]]
            responseAsProfiles.onComplete {
            println("[UNMARSH - SUCCESS] Unmarshaling Done!")
            _.get match {
              case profiles: Array[Profile] =>
                println("[UNMARSH - SUCCESS] Sending Profiles message to " + sender())
                requester ! profiles
                println("[UNMARSH - SUCCESS] Message sent to " + sender())
              case _ => println("error")
              }
            }

          case Failure(_)   =>
            sys.error("something wrong")
            //return Future[Array[Profile]]

        }
      }
      println("[RECEIVE] Message GetProfiles received from " + sender().toString())
      getProfiles
      println("[RECEIVE] Message GetProfiles invoked")

  }

Когда Актёр получает сообщение GetProfiles:

1 - он отправляет запрос на удаленный сервер, поэтому результатом операции будет Future [HttpResponse]

2 - в случае успеха он получает ответ (массив JSON) и запрашивает демаршалинг объекта в Array [Profile]. (Модель профиля не важна). Результатом метода Unmarshall является Future [Array [Profile]]

3- В случае успеха, я хочу отправить результат обратно первоначальному отправителю!

Мне удалось это сделать, но это уловка, потому что я сохраняю отправителя в переменной, которая видна в области (запросчик). Я знаю, что существует шаблон конвейера, поэтому теоретически я мог бы отправить объект responseAsProfiles обратно отправителю, но этот объект создается внутри метода onComplete объекта responseFuture. (надо, конечно, дождаться!)

Вот и все! Как я могу отправить результат обратно отправителю, используя шаблон канала в этом случае? Заранее спасибо!!!


person Salvatore Calcagno    schedule 17.09.2020    source источник


Ответы (1)


Общая идея состоит в том, что вы составляете фьючерсы с использованием map и flatMap и стараетесь по возможности избегать использования onComplete.

Посмотрите, сможете ли вы преобразовать свой код в следующие более мелкие части, а затем составить:

def getRawProfileData(): Future[HttpResponse] = {
 // ... here you make http request
}

def unmarshalProfiles(response: HttpResponse): Future[List[Profile]] = {
  // ... unmarshalling logic
}

def getProfiles(): Future[List[Profile]] = getRawProfileData().flatMape(unmarshalProfiles)

// now from receive block

case GetProfiles ⇒ getProfiles().pipeTo(sender())

person Pritam Kadam    schedule 17.09.2020
comment
Большой! Спасибо @Pritam! Он отлично работает, и я думаю, что теперь он очень чистый. - person Salvatore Calcagno; 18.09.2020