squbs-5.Akka HTTP客户端上的类固醇(Steroids)

原文地址:Accessing Other Services using HTTP or HTTPS

概貌

squbs-httpclient 项目在保持Akka Http API的同时,向Akka HTTP Host-Level Client-Side API添加了可操作化方面,以下是它添加的功能的列表

依赖

在你的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<HttpRequest, Integer>, Pair<Try<HttpResponse>, Integer>, HostConnectionPool>
    clientFlow = ClientFlow.create("sample", system, mat);

CompletionStage<Pair<Try<HttpResponse>, 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<String, Env, Optional<Endpoint>>:

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<Endpoint> 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容