抱歉,这有点长,因为我需要包含各种文件。
问题
我不确定我的设置[使用喷雾1.3.3]发生了什么。我正在尝试使用chunked请求进行文件上传,这些请求似乎按预期工作一次或两次,但由于某些原因,大多数时候actor在块处理程序的初始注册完成后从未接收到块。它只是消失在遗忘中,我的请求和日志只是一直在等待。当我运行调试器时,它完成了50次中的2次。然而,即使使用调试器,它也不会起作用。
基于与DemoService和FileUploadHandler相关的各种示例和讨论,我使用spray-can来检查接收到的HttpMessage的块,并在那时产生一个单独的路由。我使用curl with chunked encoding来测试我的输出。
请帮忙!我花了太多时间试图获得与我的用例工作路由混合的分块请求。
代码
这是我的代码:
TestApp.scala
object TestApp extends App with GlobalConfig {
implicit val system = ActorSystem("TestApp")
implicit val ec = system.dispatcher
val healthActor = system.actorOf(Props[HealthStateActor])
val routes = new HealthcheckController(healthActor).route ~
new ResourceController().route
val requestRouter = system.actorOf(Props(new HttpRequestCustomHandler(routes)))
IO(Http) ! Http.Bind(requestRouter, "0.0.0.0", HttpBindPort)
}
FileUploadActor.scala
class FileUploadActor(client: ActorRef, requestMetadata: RequestMetadata, request: HttpRequest, ctx: RequestContext)
extends Actor with ActorLogging with GlobalConfig {
import request._
var bytesWritten = 0L
var bytes: Array[Byte] = "".getBytes
// client ! CommandWrapper(SetRequestTimeout(Duration.Inf)) // cancel timeout
def receive = {
case c: MessageChunk =>
log.info(s"Got ${c.data.length} bytes of chunked request $method $uri")
bytes ++= c.data.toByteArray
bytesWritten += c.data.length
case e: ChunkedMessageEnd =>
log.info(s"Got end of chunked request $method $uri. Writing $bytesWritten bytes for upload: $requestMetadata")
Try(saveFile(requestMetadata)) match {
case Success(_) => ctx.complete(HttpResponse(StatusCodes.Created, entity = "success"))
case Failure(f) => f.printStackTrace(); ctx.complete(HttpResponse(StatusCodes.InternalServerError, entity = "failure"))
}
// client ! CommandWrapper(SetRequestTimeout(UploadRequestTimeout.seconds)) // reset timeout to original value
context.stop(self)
}
}
FileUploadService.scala
RegisterChunkHandler消息是我看到调试器在断点处停止的最后一步,以及日志安静的位置。当它工作时,我可以看到FileUploadActor收到MessageChunk消息。
trait FileUploadService extends Directives {
this: Actor with ActorLogging with GlobalConfig =>
def chunkedRoute() = {
path(resourceAPI / "upload" / "resource" / Segment) { resourceId =>
put {
detach() {
ctx => {
val request = ctx.request
val client = sender()
val handler = context.actorOf(Props(new FileUploadActor(client,
RequestMetadata(....),
request, ctx)))
sender ! RegisterChunkHandler(handler)
}
}
}
}
}
}
HttpRequestCustomHandler.scala
class HttpRequestCustomHandler(routes: Route, resourceProviderRef: ResourceProvider)
extends HttpServiceActor
with FileUploadService
with ActorLogging
with GlobalConfig {
val normal = routes
val chunked = chunkedRoute()
def resourceProvider = resourceProviderRef
val customReceive: Receive = {
// clients get connected to self (singleton handler)
case _: Http.Connected => sender ! Http.Register(self)
case r: HttpRequest =>
normal(RequestContext(r, sender(), r.uri.path).withDefaultSender(sender()))
case s@ChunkedRequestStart(HttpRequest(PUT, path, _, _, _)) =>
chunked(RequestContext(s.request, sender(), s.request.uri.path).withDefaultSender(sender()))
}
override def receive: Receive = customReceive
}
HttpRequestHandler.scala
abstract class HttpRequestHandler(routes: Route) extends HttpServiceActor{
override def receive: Receive = runRoute(routes)
}
application.conf:
spray.can.server {
request-timeout = 20 s
pipelining-limit = disabled
reaping-cycle = infinite
stats-support = off
request-chunk-aggregation-limit = 0
parsing.max-content-length = 100000000
parsing.incoming-auto-chunking-threshold-size = 15000000
chunkless-streaming = on
verbose-error-messages = on
verbose-error-logging = on
}
卷曲成功:
curl -vvv -X PUT -H "Content-Type: multipart/form-data" -d
'@/Users/abc/Documents/test.json'
'http://localhost:8180/upload/resource/02521081'
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8100 (#0)
> PUT /upload/resource/02521081 HTTP/1.1
> User-Agent: curl/7.37.1
> Host: localhost:8100
> Accept: */*
> Content-Type: multipart/form-data
> Content-Length: 82129103
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
< HTTP/1.1 201 Created
* Server spray-can/1.3.3 is not blacklisted
< Server: spray-can/1.3.3
< Date: Mon, 17 Aug 2015 07:45:58 GMT
< Content-Type: text/plain; charset=UTF-8
< Content-Length: 7
<
* Connection #0 to host localhost left intact
success
卷曲失败(永远等待):
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8100 (#0)
> PUT /upload/resource/02521081 HTTP/1.1
> User-Agent: curl/7.37.1
> Host: localhost:8100
> Accept: */*
> Transfer-Encoding: chunked
> Content-Type: multipart/form-data
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
^C
失败(永远等待):
resource 2015-08-17 01:33:09.374 [Resource] INFO akka.event.slf4j.Slf4jLogger - Slf4jLogger started
resource 2015-08-17 01:33:09.396 08:33:09.382UTC [Resource] DEBUG akka.event.EventStream main EventStream(akka://resource) - logger log1-Slf4jLogger started
resource 2015-08-17 01:33:09.404 08:33:09.383UTC [Resource] DEBUG akka.event.EventStream main EventStream(akka://resource) - Default Loggers started
resource 2015-08-17 01:33:10.160 08:33:10.159UTC [Resource] INFO spray.can.server.HttpListener Resource-akka.actor.default-dispatcher-4 akka://resource/user/IO-HTTP/listener-0 - Bound to /0.0.0.0:8100
成功(为清晰起见编辑了日志):
resource 2015-08-17 00:42:12.283 [Resource] INFO akka.event.slf4j.Slf4jLogger - Slf4jLogger started
resource 2015-08-17 00:42:12.295 07:42:12.290UTC [Resource] DEBUG akka.event.EventStream main EventStream(akka://resource) - logger log1-Slf4jLogger started
resource 2015-08-17 00:42:12.308 07:42:12.291UTC [Resource] DEBUG akka.event.EventStream main EventStream(akka://resource) - Default Loggers started
resource 2015-08-17 00:42:13.007 07:42:13.005UTC [Resource] INFO spray.can.server.HttpListener Resource-akka.actor.default-dispatcher-4 akka://resource/user/IO-HTTP/listener-0 - Bound to /0.0.0.0:8100
resource 2015-08-17 00:43:47.615 07:43:47.529UTC [Resource] DEBUG c.l.resource.actor.FileUploadActor Resource-akka.actor.default-dispatcher-7 akka://resource/user/$b/$b - Got 131072 bytes of chunked request PUT http://localhost:8100/resourcesvc/0.2/api/upload/resource/02521081-20e5-483a-929f-712a9e11d117/content/5adfb5-561d-4577-b6ad-c6f42eef98
resource 2015-08-17 00:43:49.220 07:43:49.204UTC [Resource] DEBUG c.l.resource.actor.FileUploadActor Resource-akka.actor.default-dispatcher-7 akka://resource/user/$b/$b - Got 131072 bytes of chunked request PUT http://localhost:8100/resourcesvc/0.2/api/upload/resource/02521081-20e5-483a-929f-712a9e11d117/content/5adfb5-561d-4577-b6ad-c6f42eef98
.
.
.
resource 2015-08-17 00:44:05.605 07:44:05.605UTC [Resource] DEBUG c.l.resource.actor.FileUploadActor Resource-akka.actor.default-dispatcher-7 akka://resource/user/$b/$b - Got 45263 bytes of chunked request PUT http://localhost:8100/resourcesvc/0.2/api/upload/resource/02521081-20e5-483a-929f-712a9e11d117/content/5adfb5-561d-4577-b6ad-c6f42eef98
resource 2015-08-17 00:44:05.633 07:44:05.633UTC [Resource] INFO c.l.resource.actor.FileUploadActor Resource-akka.actor.default-dispatcher-7 akka://resource/user/$b/$b - Got end of chunked request PUT http://localhost:8100/resourcesvc/0.2/api/upload/resource/02521081-20e5-483a-929f-712a9e11d117/content/5adfb5-561d-4577-b6ad-c6f42eef98. Writing 82129103 bytes for upload: RequestMetadata(...,multipart/form-data)
resource 2015-08-17 00:44:05.634 07:44:05.633UTC [Resource] DEBUG c.l.resource.actor.FileUploadActor Resource-akka.actor.default-dispatcher-7 akka://resource/user/$b/$b - actor is akka://resource/user/$b/$b, sender is Actor[akka://resource/temp/$a], client is Actor[akka://resource/temp/$a]
resource 2015-08-17 00:45:58.445 [Resource] DEBUG com.abc.resource.io.FileClient$ - UploadResult@109a69fb
resource 2015-08-17 00:45:58.445 [Resource] DEBUG com.abc.resource.io.FileClient$ - upload is done: true
如果您发现任何奇怪的事,请告诉我。演员通常会像这样消失的原因是什么?在此先感谢您的帮助!
更新
我添加了进一步的日志记录,并且看到from和to actor显然都变成了deadLetters,尽管在上面的日志行中显然不是这样。这是消息&#39; RegisterChunkHandler&#39;发送到FileUploadService.scala中的发件人。
sender ! RegisterChunkHandler(handler)
相关日志:
resource 2015 - 0 8 - 17 21: 14: 32.173 20: 14: 32.173 UTC[Resource] DEBUG c.l.a.io.HttpRequestCustomHandler Resource - akka.actor.default - dispatcher - 3 akka :// Resource / user / httpcustomactor - sender is Actor[akka :// Resource / temp / $a]
resource 2015 - 0 8 - 17 21: 14: 32.175 20: 14: 32.175 UTC[Resource] INFO akka.actor.DeadLetterActorRef A4Resource-akka.actor.default-dispatcher-6 akka://A4Resource/deadLetters - Message [spray.can.Http$RegisterChunkHandler] from Actor[akka://A4Resource/user/httpcustomactor#-1286373908] to Actor[akka://A4Resource/deadLetters] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
resource 2015 - 0 8 - 17 21: 14: 32.176 20: 14: 32.176 UTC[Resource] DEBUG c.l.resource.actor.FileUploadActor Resource - akka.actor.default - dispatcher - 7 akka :// Resource / user / httpcustomactor / $a - pre - start
知道如何避免这种情况吗?