我有未定义数量的akka-http客户端从http服务下载数据。我使用akka-http主机级连接池,因为我想自定义池,因为有长时间运行的请求通过它。
由于客户端数量未定义且动态,我不知道如何配置连接池(max-open-requests / max-connections)。另外,我可能希望连接池很小(少于客户端数量),以免损坏带宽。
因此,我想设置一个客户端流程,以便新的连接和对池的请求被反压:
1.这是否意味着我需要一个物化的客户流程?
2.如何实现尽可能多的客户端流量,这样如果没有可用的连接(来自下游的需求)请求将被回压。
我的第一次尝试是Source.single pattern,但是这种方法可以超过max-open-request并抛出异常,因为每次将请求发送到服务器时它都会创建一个新的流实例。
我的第二次尝试是Source.Queue,这种方法创建了一个流,所有请求都被排入队列:但是尽管文档源SourceQueue的OverflowStrategy背压不起作用,当它超过max-connection或max-时open-request,akka-http抛出exception
我可以使用host-level streaming fashion完成背压 并拥有一个客户流程并使用MergeHub添加新请求?
这是我的解决方案:
private lazy val poolFlow: Flow[(HttpRequest, Promise[HttpResponse]), (Try[HttpResponse], Promise[HttpResponse]), Http.HostConnectionPool] =
Http().cachedHostConnectionPool[Promise[HttpResponse]](host.split("http[s]?://").tail.head, port, connectionPoolSettings)
val ServerSink =
poolFlow.async.toMat(Sink.foreach({
case ((Success(resp), p)) => p.success(resp)
case ((Failure(e), p)) => p.failure(e)
}))(Keep.left)
// Attach a MergeHub Source to the consumer. This will materialize to a
// corresponding Sink.
val runnableGraph: RunnableGraph[Sink[(HttpRequest, Promise[HttpResponse]), NotUsed]] =
MergeHub.source[(HttpRequest, Promise[HttpResponse])](perProducerBufferSize = 16).to(ServerSink)
val toConsumer: Sink[(HttpRequest, Promise[HttpResponse]), NotUsed] = runnableGraph.run()
protected[akkahttp] def executeRequest[T](httpRequest: HttpRequest, unmarshal: HttpResponse => Future[T]): Future[T] = {
val responsePromise = Promise[HttpResponse]()
Source.single((httpRequest -> responsePromise)).runWith(toConsumer)
responsePromise.future.flatMap(handleHttpResponse(_, unmarshal))
)
}