Akka HTTP连接池在几个小时后挂起

时间:2017-02-27 09:19:09

标签: scala akka akka-http

我有一个HTTP连接池在运行几个小时后挂起:

private def createHttpPool(host: String): SourceQueue[(HttpRequest, Promise[HttpResponse])] = {
    val pool = Http().cachedHostConnectionPoolHttps[Promise[HttpResponse]](host)
    Source.queue[(HttpRequest, Promise[HttpResponse])](config.poolBuffer, OverflowStrategy.dropNew)
      .via(pool).toMat(Sink.foreach {
        case ((Success(res), p)) => p.success(res)
        case ((Failure(e), p)) => p.failure(e)
      })(Keep.left).run
  }

我用以下项目排列项目:

private def enqueue(uri: Uri): Future[HttpResponse] = {
    val promise = Promise[HttpResponse]
    val request = HttpRequest(uri = uri) -> promise

    queue.offer(request).flatMap {
      case Enqueued => promise.future
      case _ => Future.failed(ConnectionPoolDroppedRequest)
    }
}

并解决这样的反应:

private def request(uri: Uri): Future[HttpResponse] = {
    def retry = {
      Thread.sleep(config.dispatcherRetryInterval)
      logger.info(s"retrying")
      request(uri)
    }

    logger.info("req-start")
    for {
      response <- enqueue(uri)

      _ = logger.info("req-end")

      finalResponse <- response.status match {
        case TooManyRequests => retry
        case OK => Future.successful(response)
        case _ => response.entity.toStrict(10.seconds).map(s => throw Error(s.toString, uri.toString))
      }
    } yield finalResponse
}

如果Future成功,则始终会转换此函数的结果:

def get(uri: Uri): Future[Try[JValue]] = {
  for {
    response <- request(uri)
    json <- Unmarshal(response.entity).to[Try[JValue]]
  } yield json
}

一切都运行良好一段时间,然后我在日志中看到的只是req-start而没有req-end。

我的akka​​配置是这样的:

akka {
  actor.deployment.default {
    dispatcher = "my-dispatcher"
  }
}

my-dispatcher {
  type = Dispatcher
  executor = "fork-join-executor"

  fork-join-executor {
    parallelism-min = 256
    parallelism-factor = 128.0
    parallelism-max = 1024
  }
}

akka.http {
  host-connection-pool {
    max-connections = 512
    max-retries = 5
    max-open-requests = 16384
    pipelining-limit = 1
  }
}

我不确定这是配置问题还是代码问题。我的并行性和连接数如此之高,因为没有它我的req / s速率非常低(我想尽可能快地请求 - 我有其他速率限制代码来保护服务器)。

1 个答案:

答案 0 :(得分:3)

您没有使用从服务器返回的响应的实体。引用下面的文档:

  

消费(或丢弃)请求的实体是强制性的!如果   意外地离开既不消耗也不丢弃Akka HTTP将假设   传入的数据应保持反压,并将停止   通过TCP反压机制传入数据。客户应该   无论HttpResponse的状态如何,都会使用实体。

实体以Source[ByteString, _]的形式出现,需要运行以避免资源匮乏。

如果您不需要读取实体,使用实体字节的最简单方法是使用

丢弃它们
res.discardEntityBytes()

(您可以通过添加 - 例如 - .future().map(...)来附加回调。

This page in the docs描述了所有替代方法,包括如何根据需要读取字节。

---编辑

提供更多代码/信息后,显然资源消耗不是问题。此实现中还有另一个大红旗,即重试方法中的Thread.sleep。 这是一个阻塞调用,很可能会使您的基础actor系统的线程基础结构匮乏。

docs提供了关于为什么这是危险的全面解释。

尝试更改并使用akka.pattern.afterdocs)。示例如下:

def retry = akka.pattern.after(200 millis, using = system.scheduler)(request(uri))