[翻译]squbs官网之6 Akka HTTP 客户端

概述

squbs-httpclient 项目在保持Akka HTTP API的同时,增加了Akka HTTP主机级别客户端API 操作方面的内容。 下面是它添加的功能列表:

  • 服务发现:允许任何服务发现机制插入,并允许通过字符串标识符 ( 例如 paymentserv ) 解析 HTTP 端点。
  • 客户端单独配置: 让每个客户端单独覆盖application.conf中的默认值。
  • 管道:允许各客户端全局或者各自注册一个BidiAkka Stream流。
  • 指标:为每个客户端提供 Codahale 指标, 而无需 AspectJ。
  • JMX Beans:将每一个客户端的配置暴露为JMX bean。
  • 断路器:基于断路器提供基于流的弹性。

依赖

增加如下依赖到你的build.sbt或者其它scala构建文件:

"org.squbs" %% "squbs-httpclient" % squbsVersion

用法

squbs-httpclient项目坚持Akka HTTP API。唯一的例外是创建主机连接池期间。不是使用Http().cachedHostConnectionPool,它定义了ClientFlow使用一组相同的参数(和一些其它可选的参数)。

类似于Akka HTTP主机级别客户端API例子,使用ClientFlow 如下:

implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
// construct a pool client flow with context type `Int`
val poolClientFlow = ClientFlow[Int]("sample") // Only this line is specific to squbs

val responseFuture: Future[(Try[HttpResponse], Int)] =
  Source.single(HttpRequest(uri = "/") -> 42)
    .via(poolClientFlow)
    .runWith(Sink.head)

可以传递可选的设置(例如HttpsConnectionContextConnectionPoolSettings)给ClientFlow

val clientFlow = ClientFlow("sample", Some(connectionContext), Some(connectionPoolSettings))

HTTP 模型

Below is an HttpRequest creation example in Scala. Please see HTTP Model Scala documentation for more details:
下面是HttpRequest创建的例子。更详细内容请看Akka文档中的HTTP模型:

import HttpProtocols._
import MediaTypes._
import HttpCharsets._
val userData = ByteString("abc")
val authorization = headers.Authorization(BasicHttpCredentials("user", "pass"))

HttpRequest(
  PUT,
  uri = "/user",
  entity = HttpEntity(`text/plain` withCharset `UTF-8`, userData),
  headers = List(authorization),
  protocol = `HTTP/1.0`)

服务发现链

squbs-httpclient在客户端池创建的时候不要求主机名/端口组合。相反, 它允许注册服务发现链, 通过运行注册的服务发现机制, 允许通过字符串标识符解析HttpEndpoint。例如,在上面的例子中,“sample”是客户端想要连接的服务逻辑名,配置的服务发现链将它解析到包括主机和端口的HttpEndpoint, 例如, http://akka.io:80。

请注意,你仍然可以传递一个有效的http URI作为字符串给ClientFlow ,作为一个默认解析器,作为默认的服务发现链预配置的有效http URI:

ClientFlow[Int]("http://akka.io") 

下面展示了两种不同的注册解析器。闭包风格允许更紧凑、可读的代码。然而,子类具有保持状态并根据此状态做出决策的能力:

注册函数类型(String, Env) => Option[HttpEndpoint]

ResolverRegistry(system).register[HttpEndpoint]("SampleEndpointResolver", { (svcName, env) =>
  svcName match {
    case "sample" => Some(HttpEndpoint("http://akka.io:80"))
    case "google" => Some(HttpEndpoint("http://www.google.com:80"))
    case _ => None
})

注册类继承于Resolver[HttpEndpoint]

class SampleEndpointResolver extends Resolver[HttpEndpoint] {
  override def name: String = "SampleEndpointResolver"

  override def resolve(svcName: String, env: Environment): Option[HttpEndpoint] =
    svcName match {
      case "sample" => Some(Endpoint("http://akka.io:80"))
      case "google" => Some(Endpoint("http://www.google.com:80"))
      case _ => None
    }
}

// Register EndpointResolver
ResolverRegistry(system).register[HttpEndpoint](new SampleEndpointResolver)

你可以注册多个解析器。该链是按相反的注册顺序执行的。如果解析器返回 None, 则表示无法解析它, 并尝试下一个解析器。

如果已解析的端点是安全的, 例如 https, 则可以将SSLContext作为可选参数传递给HttpEndpoint

也可以将一个可选的Config传递给HttpEndpoint覆盖默认的配置。然而,客户端具体的配置始终比传入的配置有更高的优先级。

请看资源解析中关于解析的详细信息。

客户端单独配置

Akka HTTP配置定义了配置的默认值。你可以覆盖application.conf默认值;然而,这将影响所有的客户端。要执行客户端特定的覆盖,Akka HTTP在创建HostConnectionPool流的时候,传入一个ConnectionPoolSettings。这也得到了 squbs 的支持。

In addition to the above, squbs allows a client specific override in application.conf. You just need to specify a configuration section with the client's name that has type = squbs.httpclient. Then, you can specify any client configuration inside the section. For instance, if we would like to override the max-connections setting only for the above "sample" client, but no other client, we can do it as follows:
除此外,squbs允许在application.conf里定义客户端特定的覆盖。你仅需定义一个带有客户端名称的配置节,并带有type = squbs.httpclient。然后,你可以在此节中定义任意客户端配置。例如,如果想要在上面的“sample”客户端覆盖max-connections 设置,而其它的客户端不需要,可以如下操作:

sample {
  type = squbs.httpclient

  akka.http.host-connection-pool {
    max-connections = 10
  }
}

管道

我们通常需要在不同的客户端之间拥有共同的基础结构功能或组织标准。基础结构包括,但不限于,日志、指标收集、请求追踪、认证/授权、 追踪、cookie 管理、A/B测试等。随着 squbs 促进关注的分离, 此类逻辑属于基础结构, 而不是客户端实现。squbs 管道允许基础结构提供组件安装到客户端, 而无需客户自己担心这些方面。请看squbs管道更多细节。

通常讲,管道是一个Bidi Flow,作为squbs客户端和Akka HTTP层间的桥梁。squbs-httpclient允许注册一个Bidi Akka Stream流或者全局地对所有客户端,或者个别客户端。为了注册一个客户端特定管道,设置pipeline配置。可以通过defaultPipeline设置,打开或关闭默认管道(如果没有设置,设为on):

sample {
  type = squbs.httpclient
  pipeline = metricsFlow
  defaultPipeline = on
}

请看squbs管道章节了解如何创建管道和配置默认管道。

指标

squbs带有预建的管道元素用于指标收集,且squbs激活模板设置这些默认值。因此,每个 squbs 的 http 客户端都可以在没有任何代码更改或配置的情况下, 收集现成的Codahale Metrics。请注意,squbs指标收集不需要AspectJ或者其它运行时代码编写。下面指标在JMX可以默认获得:

  • 请求计时器
  • 请求计数器
  • 每个http响应码的计量器:2xx, 3xx, 4xx, 5xx
  • ClientFlow返回的每个异常类型的计量器

可以通过MetricsExtension(system).metrics访问MetricRegistry 。这使您可以创建更多的计量器, 计时器, 直方图等, 或传递给不同类型的指标报表。

JMX Bean

系统配置的可见性在解决问题时非常重要。squbs-httpclient为一个客户端注册一个JMX bean。JMX bean暴露所有的配置,例如端点、主机连接池配置,等等。bean的名称由org.squbs.configuration.${system.name}:type=squbs.httpclient,name=$name设置。所以,如果actor system名称是squbs,客户端名称是sample,那么JMX bean名称为org.squbs.configuration.squbs:type=squbs.httpclient,name=sample

断路器

squbs提供CircuitBreakerBidi Akka Streams GraphStage 来为流提供断路器功能。这是为流实现的通用断路器。请看断路器文档中详细内容。

断路器有可能改变消息的顺序,所以它需要一个可随身携带的Context,像ClientFlow。但是,除此之外,它还需要能够唯一地识别每个元素的内部机制。因此,传递给ClientFlowContext或来自Context的映射需能够唯一地标识每个元素。如果启用了断路器, 并且传递给ClientFlowContext没有唯一标识每个元素, 那么您将遇到意外的行为。请看“断路器”文档中的“上下文到唯一Id映射”节,关于提供唯一id的详细内容。

断路器默认是关闭的。请看下面的内容启动。

一旦启动,任何Failure或者带有400状态码的Success则递增失败计数。CircuitBreakerState的默认实现是AtomicCircuitBreakerState,可以跨物化和跨流共享。 这些可以通过传递CircuitBreakerSettings以编程方式进行定制。

application.conf中的配置

在客户端的具体配置中,您可以添加circuit-breaker并指定要覆盖的配置。 其余的将使用默认设置。 请参见这里的默认断路器配置。

sample {
  type = squbs.httpclient

  circuit-breaker {
    max-failures = 2
    call-timeout = 10 milliseconds
    reset-timeout = 100 seconds
  }
}

编程方式传递CircuitBreakerSettings

你可以编程方式传递一个CircuitBreakerSettings实例。这个API让你传递一个自定义CircuitBreakerState和可选的回调,失败判断和唯一id映射函数。如果编程方式传入一个CircuitBreakerSettings实例,那么断路器在application.conf中的设置将忽略。

在下面的示例中, 通过withFallback提供了回调响应。默认的失败判断是通过withFailureDecider重写,只考虑状态代码>= 500增加断路器的故障计数:

import org.squbs.streams.circuitbreaker.CircuitBreakerSettings

val circuitBreakerSettings =
  CircuitBreakerSettings[HttpRequest, HttpResponse, Int](circuitBreakerState)
    .withFallback( _ => Try(HttpResponse(entity = "Fallback Response")))
    .withFailureDecider(
      response => response.isFailure || response.get.status.intValue() >= StatusCodes.InternalServerError.intValue)

val clientFlow = ClientFlow[Int]("sample", circuitBreakerSettings = Some(circuitBreakerSettings))  

调用其它服务作为回调

断路器使用中一个常见的场景是调用另一个服务作为回调。调用另一个服务需要在回调函数中有一个新的流物化;所以,我们建议用户将失败的快速消息传递到下游,并相应地进行分支。这可以在相同的流中定义回调ClientFlow

你可能感兴趣的:([翻译]squbs官网之6 Akka HTTP 客户端)