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