使用Akka 2.4.7。我想记录整个Http响应。使用类似于How does one log Akka HTTP client requests的实现关注的代码是从HttpEntity中提取数据的代码
def entityAsString(entity: HttpEntity) (implicit m: Materializer, ex: ExecutionContext): Future[String] = {
entity.dataBytes.map(_.decodeString("UTF-8")).runWith(Sink.head)
}
如果POST请求的负载较小,则此方法很有效。但是从1K开始有一个例外:
java.lang.IllegalStateException: Substream Source cannot be materialized more than once
问题:为什么此异常取决于POST有效内容的大小。希望有任何可能的解决办法吗?
完整日志消息:
2016-08-11 10:15:35,100 ERROR aaActorSystemImpl [undefined]:处理请求HttpRequest时出错(HttpMethod(POST),http://localhost:3001/api/v2/exec,List(User-Agent:curl / 7.30.0,Host:localhost:3001,接受: / ,Expect:100-continue,Timeout-Access:),HttpEntity.Default(multipart / form-data; boundary = --------------- ---------- acebdf13572468; charset = UTF-8,5599,Source(SourceShape(StreamUtils $$ anon $ 2.out),CompositeModule [2db5bfef]
姓名:未命名的
模块:
(未命名)CompositeModule [4aac8b90]
姓名:未命名的
模块:
(SubSource%28EntitySource%29)GraphStage(EntitySource)[073d36ba]
(未命名)[155dd7c9] GraphStage(OneHundredContinueStage)[40b6c892]的副本
(未命名)[1b902132] GraphStage(Collect)[75f65c1c]的副本
(可限制的)[76375468] CompositeModule [59626a09]的副本
名称:限制
模块:
(未命名)GraphStage(未知操作)[1bee846d]
下行流:
上行流:
MatValue:忽略
下行流:
SubSource.out - > GraphStage.in
GraphStage.out - > Collect.in
Collect.out - > unknown-operation.in
上行流:
GraphStage.in - > SubSource.out
Collect.in - > GraphStage.out
unknown-operation.in - > Collect.out
MatValue:Atomic(SubSource%28EntitySource%29 [073d36ba])
(未命名)[77d6c04c] GraphStage副本(akka.http.impl.util.StreamUtils$$anon$2@30858cb0)[7e073049]
下行流:
SubSource.out - > GraphStage.in
GraphStage.out - > Collect.in
Collect.out - > unknown-operation.in
unknown-operation.out - > StreamUtils $$不久$ 2.in
上行流:
GraphStage.in - > SubSource.out
Collect.in - > GraphStage.out
unknown-operation.in - > Collect.out
StreamUtils $$ anon $ 2.in - >未知operation.out
MatValue:Atomic(akka.stream.impl.StreamLayout $ CompositeModule [4aac8b90]))),HttpProtocol(HTTP / 1.1))
java.lang.IllegalStateException:子流源不能多次实现
at akka.stream.impl.fusing.SubSource $$ anon $ 4.setCB(StreamOfStreams.scala:703)
at akka.stream.impl.fusing.SubSource $$ anon $ 4.preStart(StreamOfStreams.scala:713)
at akka.stream.impl.fusing.GraphInterpreter.init(GraphInterpreter.scala:475)
at akka.stream.impl.fusing.GraphInterpreterShell.init(ActorGraphInterpreter.scala:380)
at akka.stream.impl.fusing.ActorGraphInterpreter.tryInit(ActorGraphInterpreter.scala:538)
at akka.stream.impl.fusing.ActorGraphInterpreter.preStart(ActorGraphInterpreter.scala:586)
at akka.actor.Actor $ class.aroundPreStart(Actor.scala:489)
at akka.stream.impl.fusing.ActorGraphInterpreter.aroundPreStart(ActorGraphInterpreter.scala:529)
at akka.actor.ActorCell.create(ActorCell.scala:590)
at akka.actor.ActorCell.invokeAll $ 1(ActorCell.scala:461)
at akka.actor.ActorCell.systemInvoke(ActorCell.scala:483)
at akka.dispatch.Mailbox.processAllSystemMessages(Mailbox.scala:282)
at akka.dispatch.Mailbox.run(Mailbox.scala:223)
at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
在scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
在scala.concurrent.forkjoin.ForkJoinPool $ WorkQueue.runTask(ForkJoinPool.java:1339)
在scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
在scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
答案 0 :(得分:0)
我认为entity.dataBytes
在调用此entityAsString
之前已用于某些有用的目的,或者entityAsString
被调用两次。一般情况下,HttpEntity
的内容无法重复使用。但是,HttpEntity.Strict
的内容可以重复使用。
答案 1 :(得分:0)
我发现问题仍然与Akka-http 2.6.4相关,并且在查看了一些错误报告之后,这篇文章特别帮助我找到了解决方法https://github.com/akka/akka-http/issues/73。
但是,在上面的参考文献中提到,这意味着文件的内容存储在内存中,而不是使用流。因此,我认为这是一种解决方法,而不是解决方案。
还要注意,我在fileUpload
和storeUploadedFile
之间的行为没有发现任何不同。此工作对两者都有效。
WORKAROUND:这是我的函数示例
def createTestUploadWithStrict = toStrictEntity(3.seconds) {
(withoutSizeLimit &
post &
pathPrefix("test") &
fileUpload("data") &
formField("f1".as[MyCustomFormFiled])){
case ((metadata: FileInfo, fileStream: Source[ByteString, Any]), di:MyCustomFormFiled) => {
// Save the file
val file = tempDestination(metadata)
val sink = FileIO.toPath(file.toPath)
val writeResultFut = fileStream.runWith(FileIO.toPath(file.toPath))
val result = ???
// file is written to file
onComplete(writeResultFut) {
case Success(_) =>
complete(200 -> s"Working fine with data $result")
case Failure(e) =>
complete(500 -> s"Error while writing data file: $e")
}
}
}
}
并使用storeUploadedFile
:
def createTestUploadWithStrict = toStrictEntity(3.seconds) {
(withoutSizeLimit &
post &
pathPrefix("test2") &
storeUploadedFile("data", tempDestination) &
formField("device".as[MyCustomFormFiled])){
(metadata: FileInfo, file: File, di:MyCustomFormFiled) =>
val result = ???
complete(200 -> s"Working fine with data $result")
}
}
请注意,Unmarshaller隐式提供:
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import spray.json._
trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol{
implicit val mycustomFormFieldFormat = jsonFormat2(MyCustomFormFiled) }