Akka HTTP Future回应太晚了

时间:2018-03-07 14:20:14

标签: scala akka-http

我想禁止使用varnish的URL。我首先要做的是从领事那里收集所有健康的知识产权。

private def nodeInfo:Future[List[NodeInfo]] = {
  val request = HttpRequest(method = HttpMethods.GET, uri = consulUrl)

  Future.successful {
    Http().singleRequest(request).flatMap(response =>
      response.status match {
        case OK => Unmarshal(response).to[List[NodeInfo]]
        case _ =>
          response.entity.toStrict(5.seconds).flatMap { entity =>
            val body = entity.data.decodeString("UTF-8")
            log.warning(errorMessage(request, response, body))
            Future.failed(new IOException(errorMessage(response, body)))
          }
      })
  }.flatMap(value => value)
}

这可以按预期工作。 在理解的帮助下,我想遍历所有这些。

def banFromCache(toBanUrl:String): Future[String] = {
  for {
    nodes <- nodeInfo
    result <- loopNodes(nodes, toBanUrl)
  } yield result
}

使用foreach循环,我发送HttpRequest并获取每个HttpResponses。但由Future函数引起的结果是在请求完成之前完成。

private def loopNodes(nodes:List[NodeInfo], toBanUrl:String):Future[String] = Future {
  val banResult = new ListBuffer[String]

  nodes.foreach(node => {
    banAllHealthy(node, toBanUrl).onComplete {
      case Failure(err) =>
        banResult += node.Node.Address + " " + err.getMessage
        log.error("Request failed: " + node.Node.Address + " " + err.getMessage)
      case Success(res) =>
        banResult += node.Node.Address + " " + res.toString
        log.info("Request success: " + node.Node.Address + " " + res.toString)
    }
  })

  banResult.toList.toString()
}

private def banAllHealthy(nodeInfo:NodeInfo, toBanUrl: String):Future[HttpResponse] = {
  def request(): Future[HttpResponse] =
    Http().singleRequest(HttpRequest(method = HttpMethods.GET, uri = "http://localhost:9000/healthcheck"))
    //Http().singleRequest(HttpRequest(method = HttpMethods.GET, uri = "http://" + nodeInfo.Node.Address + "/" + toBanUrl))

  val responseFuture: Future[HttpResponse] = request()
  responseFuture
}

这里的路线非常简单:

} ~ pathPrefix("ban") {
      pathPrefix(Segment) { banpath =>
        pathEndOrSingleSlash {
          get {
            complete(banFromCache(banpath).map(_.asJson))
          }
        }
      }

有没有办法一次显示所有回复?

2 个答案:

答案 0 :(得分:1)

要累积结果字符串,请保持在Future

的上下文中
private def loopNodes(nodes: List[NodeInfo], toBanUrl: String): Future[String] = {

  val futures: List[Future[String]] = nodes.map { node =>
    banAllHealthy(node, toBanUrl)
      .map(res => s"${node.Node.Address} ${res}")
      .recover { case err => s"${node.Node.Address} ${err.getMessage}" }
  }

  Future.reduceLeft(futures)(_ + "\n" + _)
}

答案 1 :(得分:0)

如果你把回调放在处理每个未来的结果上,你将失去对执行的控制。随着foreach,每个未来都生活在自己的并行执行中。父母未来的收益是因为它不会等待任何事情。我建议不要使用ListBuffer并使用更不可变的样式。无论如何,尝试将整个计算构建为封装整个计算的一个未来:

private def loopNodes(nodes:List[NodeInfo], toBanUrl:String):Future[String] = {
  nodes.map { node =>
      // Creates a tuple to store current node and http result
      // (Node, result, HttpResult)
      (node, "", banAllHealthy(node, toBanUrl))
  }.foldLeft(Future(""))((str, b) =>
      b match {
        case (node, str ,response) => {
          // Each response will be transformed to string
          (response map (result => str  + " " + node.Node.Address + " " + "Success"))
          // In case of node is not available its suppose that the HttpClient will raise an execption  
          .recover {
            case err: Throwable  =>
              str + " " + node.Node.Address + " " + "Error " + err.getMessage
            case _ =>
              str  + " " + node.Node.Address + " " + "Unknown Error"
          }
        }
    })
}