在scala spray框架中,如何创建具有不同配置(例如超时、重试)的多个http客户端

在scala spray框架中,如何创建具有不同配置(例如超时、重试)的多个http客户端,scala,spray,Scala,Spray,我有两个spray http客户端,如下所示: val pipelineFoo: HttpRequest => Future[Foo] = ( sendReceive ~> unmarshal[Message.Foo]) val pipelineBar: HttpRequest => Future[Bar] = ( sendReceive ~> unmarshal[Message.Bar]) def execFoo(h: S

我有两个spray http客户端,如下所示:

  val pipelineFoo: HttpRequest => Future[Foo] = (
    sendReceive
    ~> unmarshal[Message.Foo])

  val pipelineBar: HttpRequest => Future[Bar] = (
    sendReceive
    ~> unmarshal[Message.Bar])

  def execFoo(h: String, p: Int): Future[Foo] = {
    val uri = Uri.from(scheme = "http", host = h, port = p, path = "/foo")
    pipelineFoo(Get(uri))
  }

  def execBar(h: String, p: Int): Future[Bar] = {
    val uri = Uri.from(scheme = "http", host = h, port = p, path = "/bar")
    pipelineBar(Get(uri))
  }

我想让foo请求在长时间超时的情况下重试几次,让bar请求不重试并有短时间的超时(比如1秒)。如何在spray中实现这一点(很抱歉,如果文档中有这一点,但我找不到它——我只找到了一些关于全局设置此类配置参数的文档)。

这应该不会太难。sendReceive实际上可以接受更多参数。例如,以下是其中一个备选方案的签名:

def sendReceive(transport: ActorRef)(implicit ec: ExecutionContext, futureTimeout: Timeout): SendReceive
我自己也在类似的场景中使用它,当我使用外部服务时,与使用内部服务时,我必须有更多的重试次数和更长的超时时间

下面是我使用的管道示例:

lazy val pipeline: HttpRequest => Future[HttpResponse] = (
addCredentials(BasicHttpCredentials(clientConnection.credentials._1, clientConnection.credentials._2))
  ~> addHeader(`User-Agent`(ProductVersion("<YOUR NAME HERE>", "<YOUR VERSION HERE>", "http://github.com/<WHEREVER YOUR PROJECT IS>"), ProductVersion("spray-client", "1.3.1", "http://spray.io")))
  ~> logRequest(log)
  ~> sendReceive(clientConnection.connection)(clientConnection.context, clientConnection.timeout)
  ~> decode(Deflate)
  ~> decode(Gzip)
)
lazy val管道:HttpRequest=>Future[HttpResponse]=(
addCredentials(BasicHttpCredentials(clientConnection.credentials.\u 1,clientConnection.credentials.\u 2))
~>addHeader(`User Agent`(产品版本(“,”,”http://github.com/),产品版本(“喷涂客户端”、“1.3.1”http://spray.io")))
~>logRequest(日志)
~>sendReceive(clientConnection.connection)(clientConnection.context,clientConnection.timeout)
~>解码(放气)
~>解码(Gzip)
)

clientConnection没什么特别的。这只是我制作的一个案例类,可以通过代码或应用程序中的一些配置手动填充。两年后,conf

。 我们也有同样的需求,我们的解决方案基于Spray connector文件的拷贝/粘贴

import akka.actor.{ActorRef, ActorSystem}
import akka.io.IO
import akka.pattern.ask
import com.typesafe.config.Config
import spray.can.Http
import spray.can.Http.HostConnectorSetup
import spray.can.client.HostConnectorSettings
import spray.client.pipelining.sendReceive
import spray.http.Uri.Host
import spray.http.{HttpRequest, HttpResponse, Uri}

import scala.concurrent.duration._
import scala.concurrent.{ExecutionContextExecutor, Future}

case class HttpCustomSettings(
  requestTimeout: Duration,
  maxRetries:     Int,
  maxConnections: Int
)

/**
 * Implement a new HTTP client on top of akka IO and spray HTTP
 * to provide a way for caller to set client parameters on request basis instead
 * of globally in application.conf
 *
 * This client defaults all its configuration with the one set in spray.conf
 * see spray.can.client and spray.can.host-connector
 * But you can override some of them on demand
 * - maxRetries
 * - requestTimeout
 * - maxConnections
 */
class HttpClient(actorSystem: ActorSystem, config: Config) {
  private implicit val context: ActorSystem = actorSystem
  private implicit val dispatcher: ExecutionContextExecutor = actorSystem.dispatcher

  private val HTTP = "http"
  private val HTTPS = "https"

  private val defaultSettings: HostConnectorSettings =
    HostConnectorSettings.fromSubConfig(config.getConfig("spray.can"))

  //not configurable since this timeout has little to no use practically
  //this timeout DOES NOT kill the open connection
  //http://kamon.io/teamblog/2014/11/02/understanding-spray-client-timeout-settings/
  private implicit val clientFutureTimeout: akka.util.Timeout = 5.seconds

  def send(
    request:        HttpRequest,
    customSettings: Option[HttpCustomSettings] = None
  ): Future[HttpResponse] = {
    val pipeline: Future[HttpRequest ⇒ Future[HttpResponse]] =
      pipelineForUri(request.uri, customSettings)

    pipeline.flatMap(send ⇒ send(request))
  }

  /**
   * To understand more this method
   * @see http://kamon.io/assets/img/diagrams/spray-client-actors.png
   * @see [[spray.can.HttpManager]]
   * @see [[spray.can.client.HttpHostConnector]]
   * @see [[spray.can.Http]]
   */
  private def pipelineForUri(
    uri:            Uri,
    customSettings: Option[HttpCustomSettings]
  ): Future[HttpRequest ⇒ Future[HttpResponse]] = {
    for { 
      Http.HostConnectorInfo(connector, _) ← IO(Http) ? connectorSetup(uri, customSettings) 
} yield sendReceive(connector)
  }

  private def connectorSetup(
    uri:            Uri,
    customSettings: Option[HttpCustomSettings]
  ): HostConnectorSetup = {
    require(
      uri.scheme == HTTP || uri.scheme == HTTPS,
      s"Not a valid $HTTP URI scheme: '${uri.scheme}' in '$uri'. (Did you forget $HTTP:// ?)"
    )

    val connector: HostConnectorSetup = HostConnectorSetup(
      uri.authority.host.toString,
      uri.effectivePort,
      sslEncryption = uri.scheme == HTTPS
    )

    customSettings match {
      case Some(custom) ⇒ connector.copy(settings = Option(mapCustomSettings(defaultSettings, custom)))
      case None         ⇒ connector.copy(settings = Option(defaultSettings))
    }
  }

  private def mapCustomSettings(
    settings:       HostConnectorSettings,
    customSettings: HttpCustomSettings
  ): HostConnectorSettings = {
    settings.copy(
      maxRetries = customSettings.maxRetries,
      maxConnections = customSettings.maxConnections,
      connectionSettings = settings.connectionSettings.copy(requestTimeout = customSettings.requestTimeout)
    )
  }

}

@拉里安德森:你说得对。我从未在代码中设置重试次数。可能有办法,但我通常只是在
application.conf
中的
spray.can.host connector
中设置
max retries
,这并不是以编程方式更改任何spray.can.host-connector,这只是显式地设置ActorRFALSO的超时,如果设置了requestTimeout,则覆盖收割周期可能很有用