如何使用akka-http 2.4.6读取网页,作为线条流

时间:2016-05-24 14:34:48

标签: scala akka-stream akka-http

我在GitHub中完成了一个示例项目:akauppi/akka-2.4.6-trial

我想要的东西看起来很简单:读取一个URL,将内容作为String s的逐行流提供。现在我在整个中努力解决这个问题(并阅读文档),所以决定将样本公开并寻求帮助。

我对Scala很满意。我知道Akka,上次我使用Akka-stream时可能是2.4之前。现在,我迷路了。

问题:

在这些lines上,我想返回Source[String,Any],而不是Future(注意:这些行不能编译)。

问题可能是Http().singleRequest(...)实现了流程,我不希望这样。如何在不实际阅读的情况下注入阅读网页的“秘诀”?

 def sourceAsByteString(url: URL)(implicit as: ActorSystem, mat: Materializer): Source[ByteString, Any] = {
    import as.dispatcher

    val req: HttpRequest = HttpRequest( uri = url.toString )

    val tmp: Source[ByteString, Any] = Http().singleRequest(req).map( resp => resp.entity.dataBytes )   // does not compile, gives a 'Future'
    tmp
  }

3 个答案:

答案 0 :(得分:2)

问题是你从服务器获得的块不是行,但可能是任何东西。您通常会在一个块中获得小响应。所以你必须自己将流分割成行。

这样的事情应该有效:

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.client.RequestBuilding._
import akka.stream.ActorMaterializer

implicit val system = ActorSystem("test")

implicit val mat = ActorMaterializer()

val delimiter: Flow[ByteString, ByteString, NotUsed] =
  Framing.delimiter(
    ByteString("\r\n"),
    maximumFrameLength = 100000,
    allowTruncation = true)

import system.dispatcher

val f = Http().singleRequest(Get("http://www.google.com")).flatMap { res =>
  val lines = res.entity.dataBytes.via(delimiter).map(_.utf8String)
  lines.runForeach { line =>
    println(line)
  }
}

f.foreach { _ =>
  system.terminate()
}

请注意,如果您想要返回行而不是打印它们,那么最终会得到Future[Source[String, Any]],这是不可避免的,因为akka-http中的所有内容都是异步的。你可以"压扁"这是一个Source[String, Any],在请求失败的情况下不会产生任何元素,但这可能不是一个好主意。

获得一个"食谱"对于阅读网页,您可以使用Http().outgoingConnection("http://www.google.com")创建Flow[HttpRequest, HttpResponse, Future[OutgoingConnection]],这样您就可以放入HttpRequest个对象并获取HttpResponse个对象。

答案 1 :(得分:0)

  

问题可能是@实现了   流动,我不希望这样。

这确实是问题的核心。有两种方法可以开始:

Http().singleRequest(...)导致一个未来(即已经在最开始实现流)。

Http().singleRequest(...)会导致来源(非物化)。

http://doc.akka.io/docs/akka/2.4.7/scala/http/client-side/connection-level.html#connection-level-api

理想情况下,在所使用的方法的名称中可以看到这样一个重要的区别,但事实并非如此。人们只需知道它,并理解上述两种方法实际上是截然不同的。

  • @RüdigerKlaehn的回答很好地涵盖了线条切割,但也看到了Cookbook
  • 在处理Source.single(HttpRequest(...))时,使用Source代替mapConcat(期货),以展平flatMap(这是一个内部流)。拥有这两个级别的流(请求,然后是每个请求的块)会增加心理复杂性,特别是因为我们只有一个外部实体。

可能还有一些更简单的方法,但我不再那么积极地看待它了。事情很有效。也许一旦我对akka流更加流利,我会建议进一步的解决方案。

答案 2 :(得分:0)

用于读取HTTP响应的代码,linewise(akka-http 1.1.0-RC2):

val req: HttpRequest = ???

val fut: Future[Source[String,_]] = Http().singleRequest(req).map { resp =>
  resp.entity.dataBytes.via(delimiter)
    .map(_.utf8String)
}

delimiter和@Rüdiger-klaehn的回答一样。