原文地址:Accessing Other Services using HTTP or HTTPS
概貌
squbs-httpclient
项目在保持Akka Http API的同时,向Akka HTTP Host-Level Client-Side API添加了可操作化方面,以下是它添加的功能的列表
Service Discovery: 允许插入任何服务发现机制,并且允许通过字符标识符解析断点(endpoint),例如:
paymentserv
.Per Client configuration: 允许每个客户端单独覆盖
application.conf
中的默认值Metrics: 为每个客户端提供开箱即用的Codahale Metrics
JMX Beans: 通过JMX beans的方式暴露每个客户端的配置
Circuit Breaker: // TODO In progress
依赖
在你的build.sbt或scala构建文件中加入如下依赖:
"org.squbs" %% "squbs-httpclient" % squbsVersion
用法
squbs-httpclient项目坚持 Akka HTTP API。唯一的例外时在创建主机连接池期间。代替 Http().cachedHostConnectionPool
,它使用一组参数定义 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)
同时,类似在Akka HTTP Host-Level Client-Side API中的例子,ClientFlow
在JAVA中的使用如下:
final ActorSystem system = ActorSystem.create();
final ActorMaterializer mat = ActorMaterializer.create(system);
final Flow, Pair, Integer>, HostConnectionPool>
clientFlow = ClientFlow.create("sample", system, mat);
CompletionStage, Integer>> =
Source
.single(Pair.create(request, 42))
.via(clientFlow)
.runWith(Sink.head(), mat);
HTTP 模型
Scala
以下是一个HttpRequest
Scala的创建例子,更多请查阅 HTTP Model Scala documentation
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`)
Java
以下是一个HttpRequest
Java的创建例子,更多请查看Http Model Java documentation :
import HttpProtocols.*;
import MediaTypes.*;
Authorization authorization = Authorization.basic("user", "pass");
HttpRequest complexRequest =
HttpRequest.PUT("/user")
.withEntity(HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, "abc"))
.addHeader(authorization)
.withProtocol(HttpProtocols.HTTP_1_0);
服务发现链
在创建池的时候,squbs-httpclient
不需要提供主机名/端口的组合。取而代之的是,它允许注册服务发现链,通过注册服务发现机制,允许通过string识别符来解析端点(endpoint)。举个例子,在上面的例子中, "sample"
是客户端想要访问的服务的逻辑名称,配置的服务发现链会将它解析成一个包含主机名和端口的端点(endpoint),例如: http://akka.io:80
.。
这里有两个不同的注册端点解释器如下。闭包的风格允许更加紧凑和可读的代码。然而,这个子类拥有保持状态和基于这个状态做解决决定的能力。
Scala
注册函数类型 (String, Env) => Option[Endpoint]
:
EndpointResolverRegistry(system).register("SampleEndpointResolver", { (svcName, env) =>
svcName match {
case "sample" => Some(Endpoint("http://akka.io:80"))
case "google" => Some(Endpoint("http://www.google.com:80"))
case _ => None
})
注册类继承于 EndpointResolver
:
class SampleEndpointResolver extends EndpointResolver {
override def name: String = "SampleEndpointResolver"
override def resolve(svcName: String, env: Environment): Option[Endpoint] = svcName match {
case "sample" => Some(Endpoint("http://akka.io:80"))
case "google" => Some(Endpoint("http://www.google.com:80"))
case _ => None
}
}
// Register EndpointResolver
EndpointResolverRegistry(system).register(new SampleEndpointResolver)
Java
注册BiFunction
:
EndpointResolverRegistry.get(system).register("SampleEndpointResolver", (svcName, env) -> {
if ("sample".equals(svcName)) {
return Optional.of(Endpoint.create("http://akka.io:80"));
} else if ("google".equals(svcName))
return Optional.of(Endpoint.create("http://www.google.com:80"));
} else {
return Optional.empty();
}
});
注册类继承AbstractEndpointResolver
:
class SampleEndpointResolver extends AbstractEndpointResolver {
String name() {
return "SampleEndpointResolver";
}
Optional resolve(svcName: String, env: Environment) {
if ("sample".equals(svcName)) {
return Optional.of(Endpoint.create("http://akka.io:80"));
} else if ("google".equals(svcName))
return Optional.of(Endpoint.create("http://www.google.com:80"));
} else {
return Optional.empty();
}
}
// Register EndpointResolver
EndpointResolverRegistry.get(system).register(new SampleEndpointResolver());
你可以注册多个EndpointResolver
。这个链是根据注册的逆序执行。如果一个解析器返回None
,这就意味着它无法解释,下一个解释器尝试解释。
如果解释的端点(endpoint)是安全的,例如(https),可以将SSLContext
传递给Endpoint
。
每个客户端的配置(Per Client Configuration)
Akka HTTP Configuration 定义了配置的默认值。你可以在application.conf
中重写这些默认值;然而这样会影响所有的客户端。做一个特定客户端的重写,在HostConnectionPool
流创建时,Akka HTTP允许在HostConnectionPool
流创建期间传递ConnectionPoolSettings
。这也是由squbs支持。
除了以上之外,squbs还允许客户端特定重写 application.conf
。你仅需要通过客户端名称提出一个有 type = squbs.httpclient
的配置部分。然后,你可以在这个部分提出任何的客户端配置。例如,如果我们只想要重写上面"sample"
客户端中的 max-connections
设置,我们可以如下做:
sample {
type = squbs.httpclient
akka.http.host-connection-pool {
max-connections = 10
}
}
管道(Pipeline)
我们通常需要公用的基础功能或不同客户端的组织标准。这些基础设施包括而不限于,日志、指标手机、请求跟踪、认证\授权,跟踪,cookie管理,A/B测试等等。正因squbs促成关注分离,这样的逻辑将属于基础设置,而非客户端实现。 squbs pipeline 允许基础设施提供组件,安装在客户端,客户端所有者不需要担心这些方面。更多详细可查看 squbs pipeline。
一般来说,一个pipeline是一个双向流,构建了squbs客户端和Akka HTTP 层的桥梁。squbs-httpclient
允许为所有或个别的客户端注册全局双向流(默认 pipeline)。注册一个特定客户端pipeline,设置 pipeline
配置。你可以通过defaultPipeline
设置开启/关闭默认pipeline(如果未指定,则设置为on
)。
sample {
type = squbs.httpclient
pipeline = metricsFlow
defaultPipeline = on
}
请参照 squbs pipeline来查看如何创建pipeline和配置默认pipeline
指标(Metrics)
squbs带有预编译pipeline指标集,squbs激活模板将这些设为默认。因此,不需要任何代码变更或配置,每个squbs http客户端可以收集Codahale Metrics即开即用。默认情况下,JMX提供以下指标:
- 请求计时器(Request Timer)
- 请求计数仪表(Request Count Meter)
- 为每个http相应状态归类的仪表:2xx, 3xx, 4xx, 5xx
-
ClientFlow
.返回的每个异常类型的仪表
JMX Beans
故障排除问题时,可视化系统配置是非常重要的。squbs-httpclient
为每个客户端注册了JMX bean。JMX bean发布所有的配置,例如端点(endpoint),主机连接池设置等。bean的名称设置如 org.squbs.configuration.${system.name}:type=squbs.httpclient,name=$name
。所以,如果actor系统名称是 squbs
,客户端名称为 sample
,那么JMX bean的名称为org.squbs.configuration.squbs:type=squbs.httpclient,name=sample
。
Circuit Breaker
// TODO In progress