Akka 如果实体大小>;1K

Akka 如果实体大小>;1K,akka,akka-stream,Akka,Akka Stream,使用Akka 2.4.7。我想记录整个Http响应。使用类似于关注代码的实现是从HttpEntity中提取数据的实现 def entityAsString(entity: HttpEntity) (implicit m: Materializer, ex: ExecutionContext): Future[String] = { entity.dataBytes.map(_.decodeString("UTF-8")).runWith(Sink.head) } 如果POST请求的有效

使用Akka 2.4.7。我想记录整个Http响应。使用类似于关注代码的实现是从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 a.a.ActorSystemImpl [undefined]: Error during processing of request HttpRequest(HttpMethod(POST),http://localhost:3001/api/v2/exec,List(User-Agent: curl/7.30.0, Host: localhost:3001, Accept: */*, Expect: 100-continue, Timeout-Access: <function1>),HttpEntity.Default(multipart/form-data; boundary=-------------------------acebdf13572468; charset=UTF-8,5599,Source(SourceShape(StreamUtils$$anon$2.out), CompositeModule [2db5bfef]  
  Name: unnamed  
  Modules:  
    (unnamed) CompositeModule [4aac8b90]  
      Name: unnamed  
      Modules:  
        (SubSource%28EntitySource%29) GraphStage(EntitySource) [073d36ba]  
        (unnamed) [155dd7c9] copy of GraphStage(OneHundredContinueStage) [40b6c892]  
        (unnamed) [1b902132] copy of GraphStage(Collect) [75f65c1c]  
        (limitable) [76375468] copy of CompositeModule [59626a09]  
          Name: limitable  
          Modules:  
            (unnamed) GraphStage(unknown-operation) [1bee846d]  
          Downstreams:   
          Upstreams:   
          MatValue: Ignore  
      Downstreams:   
        SubSource.out -> GraphStage.in  
        GraphStage.out -> Collect.in  
        Collect.out -> unknown-operation.in  
      Upstreams:   
        GraphStage.in -> SubSource.out  
        Collect.in -> GraphStage.out  
        unknown-operation.in -> Collect.out  
      MatValue: Atomic(SubSource%28EntitySource%29[073d36ba])  
    (unnamed) [77d6c04c] copy of GraphStage(akka.http.impl.util.StreamUtils$$anon$2@30858cb0) [7e073049]  
  Downstreams:   
    SubSource.out -> GraphStage.in  
    GraphStage.out -> Collect.in  
    Collect.out -> unknown-operation.in  
    unknown-operation.out -> StreamUtils$$anon$2.in  
  Upstreams:   
    GraphStage.in -> SubSource.out  
    Collect.in -> GraphStage.out  
    unknown-operation.in -> Collect.out  
    StreamUtils$$anon$2.in -> unknown-operation.out  
  MatValue: Atomic(akka.stream.impl.StreamLayout$CompositeModule[4aac8b90]))),HttpProtocol(HTTP/1.1))  
java.lang.IllegalStateException: Substream Source cannot be materialized more than once  
    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)  
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)  
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)  
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)  
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)  
2016-08-11 10:15:35100错误a.a.ActorSystemImpl[未定义]:处理请求HttpRequest(HttpMethod(POST)时出错,http://localhost:3001/api/v2/exec,列表(用户代理:curl/7.30.0,主机:localhost:3001,接受:*/*,期望:100继续,超时访问:),HttpEntity.Default(多部分/表单数据;边界=----------------------------acebdf13572468;字符集=UTF-85599,源(SourceShape(StreamUtils$$anon$2.out),CompositeModule[2db5bfef]
姓名:无名
模块:
(未命名)复合模块[4aac8b90]
姓名:无名
模块:
(子资源%28EntitySource%29)GraphStage(EntitySource)[073d36ba]
(未命名)[155dd7c9]GraphStage副本(100个连续阶段)[40b6c892]
(未命名)[1b902132]GraphStage副本(收集)[75f65c1c]
(可限制)[76375468]复合模块副本[59626a09]
名称:limitable
模块:
(未命名)GraphStage(未知操作)[1bee846d]
下游:
上游:
MatValue:忽略
下游:
子源输出->图形阶段输入
GraphStage.out->Collect.in
Collect.out->unknown-operation.in
上游:
图阶段输入->子源输出
Collect.in->GraphStage.out
未知-operation.in->Collect.out
MatValue:原子(子源%28EntitySource%29[073d36ba])
(未命名)[77d6c04c]GraphStage的副本(akka.http.impl.util.StreamUtils$$anon$2@30858cb0)[7e073049]
下游:
子源输出->图形阶段输入
GraphStage.out->Collect.in
Collect.out->unknown-operation.in
未知-operation.out->StreamUtils$$anon$2.in
上游:
图阶段输入->子源输出
Collect.in->GraphStage.out
未知-operation.in->Collect.out
StreamUtils$$anon$2.in->unknown-operation.out
MatValue:Atomic(akka.stream.impl.StreamLayout$CompositeModule[4aac8b90])、HttpProtocol(HTTP/1.1))
java.lang.IllegalStateException:子流源不能被物化多次
在akka.stream.impl.fusing.SubSource$$anon$4.setCB(StreamOfStreams.scala:703)
在akka.stream.impl.fusing.SubSource$$anon$4.preStart(StreamOfStreams.scala:713)
在akka.stream.impl.fusing.graphinterpeter.init(graphinterpeter.scala:475)
在akka.stream.impl.fusing.graphinterperterShell.init(ActorGraphExplorer.scala:380)
在akka.stream.impl.fusing.ActorGraphTranslator.tryInit(ActorGraphTranslator.scala:538)
在akka.stream.impl.fusing.ActorGraphTranslator.preStart(ActorGraphTranslator.scala:586)
在akka.actor.actor$class.aroundPreStart(actor.scala:489)
在akka.stream.impl.fusing.ActorGraphTranslator.aroundPreStart(ActorGraphTranslator.scala:529)
在akka.actor.ActorCell.create(ActorCell.scala:590)
在akka.actor.ActorCell.invokeAll$1(ActorCell.scala:461)
在akka.actor.ActorCell.systemInvoke(ActorCell.scala:483)
在akka.dispatch.Mailbox.processAllSystemMessages上(Mailbox.scala:282)
在akka.dispatch.Mailbox.run(Mailbox.scala:223)
在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)中

我假设
实体。在调用此
实体字符串之前,数据字节已经用于一些有用的目的,或者
实体字符串
被调用两次。在一般情况下,
HttpEntity
的内容不能重用。但是,
HttpEntity.Strict
的内容可以重用。

我假设
entity.dataBytes
在调用这个
entityAsString
之前已经被用于一些有用的目的,或者
entityAsString
被调用了两次。在一般情况下,
HttpEntity
的内容不能被重用。但是,
HttpEntity.Strict
的内容可以被重用。

我发现这些问题仍然与AKKAHTTP2.6.4,在查看了一些bug报告之后,这篇文章特别帮助我找到了解决方案

但是,在上面的引用中提到,它意味着<强>文件的内容被存储在内存中,而不是使用流。因此,我认为这是解决方案而不是解决方案。

还要注意的是,我没有发现
fileUpload
storeUploadedFile
之间的行为有任何不同。这两种方法都适用

解决方法:以下是我的函数示例

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")
    }
  }
请注意,解组器隐式提供了:

import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport                                                                                                                      
import spray.json._  

trait JsonSupport extends SprayJsonSupport with  DefaultJsonProtocol{
    implicit val mycustomFormFieldFormat = jsonFormat2(MyCustomFormFiled)                                                                                                                        }  

我发现这些问题仍然与Akka http 2.6.4相关,在查看了一些错误报告之后,这篇文章特别帮助我找到了解决方案

但是,在上面的引用中提到,它意味着<强>文件的内容被存储在内存中,而不是使用流。因此,我认为这是解决方案而不是解决方案。

还要注意的是,我没有发现
fileUpload
storeUploadedFile
之间的行为有任何不同。这两种方法都适用

解决方法:以下是我的函数示例

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")
      }
    }
  }
}
并使用