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

Scala Akka HTTP连接池在几个小时后挂起,scala,akka,akka-http,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[HttpRespon

我有一个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
}
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速率很低(我想尽快请求-我有其他速率限制代码来保护服务器)。

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

必须使用(或丢弃)请求的实体!如果 意外离开既不会消耗也不会丢弃Akka HTTP 传入的数据应保持背压,并将使系统暂停 通过TCP背压机制传入数据。客户应该 使用实体,而不考虑HttpResponse的状态

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

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

res.discardEntityBytes()
(您可以通过添加-e.g.-
.future().map(…)
)附加回调)

描述所有替代方法,包括如何在需要时读取字节

---编辑

在提供了更多的代码/信息之后,很明显资源消耗不是问题所在。在这个实现中还有另一个大的危险信号,即重试方法中的
线程.sleep
。 这是一个阻塞调用,很可能会耗尽底层actor系统的线程基础结构

关于为什么这是危险的完整解释已在中提供

尝试更改它,并在()之后使用
akka.pattern。示例如下:

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

在我得到响应后,我实际上会使用实体。我将用更多信息更新帖子。只是将代码更改为使用akka.pattern.after,如果问题再次出现,我将发布更新。不过,我分析了前面的Thread.sleep代码,分析程序显示,当它停止工作时,没有一个线程处于睡眠状态。每当我得到一个429,jvisualvm就会显示其中一个线程正在休眠大约500毫秒,然后该线程又开始运行,所以我有点怀疑使用调度程序是否能够修复它。不过,使用Thread.sleep真的很糟糕-谢谢你给了我一个很好的解决方案来解决这个问题。我遇到了同样的问题。这是线程转储:
def retry = akka.pattern.after(200 millis, using = system.scheduler)(request(uri))