插件

linkerd 构建在模块化插件系统上,以便可以替换各个组件而无需重新编译。这也允许任何人构建定制化插件,实现特定于他们需要的功能。本指南将向您展示如何编写自己的自定义的 linkerd 插件,如何打包它以及如何将它安装在 linker 中。

在本指南中,我们将编写一个自定义 HTTP 响应分类器。当然,本指南中的想法也适用于编写自定义identifier,namer,名称解释器,协议或任何其他 linkerd 插件。

本指南中的所有插件代码均可在Github上获得。我建议您在阅读本指南时将代码放在另一个窗口中,以便您可以跟随。

概述

我们将编写一个自定义的HTTP响应分类器,它根据一个特殊的响应头而不是HTTP响应代码对响应进行分类。如果header的值为“成功”,我们将把响应视为成功,如果是“重试”,我们将其视为可重试的失败,否则我们将其视为不可重试的失败。

我们将描述如何编写插件,如何构建和打包它,以及如何在linkerd中安装它。

编写插件

虽然 linkerd 本身是用 scala 编写的,但是它的插件可以用 scala 或 java 编写。为了演示这个,我们将在 java 中编写我们的插件。为了使我们的插件正常工作,我们将需要3个东西:响应分类器本身,一个配置类和一个配置初始化器。所有插件都遵循这个模式:有实现业务逻辑的类,有配置类,和有配置初始化器。

响应分类器

HeaderClassifier.java 是响应分类器本身。响应分类器必须继承 PartialFunction [ReqRep,ResponseClass]。 每个插件类型都有一个不同的必须实现的接口。例如,namer插件必须实现Namer,标识符插件必须实现 Identifier

配置类

接下来,我们需要一个类来定义此插件的配置块的结构,并构建响应分类器。我们将调用这个HeaderClassifierConfig.java。 请注意,HeaderClassifierConfig 必须实现 ResponseClassifierConfigResponseClassifierConfigs 由Jackson的 linkerd config 的响应分类器部分进行反序列化,其公共成员由相应的json(或yaml)属性填充(在scala中,这将是一个case类)。在我们的例子中,我们有一个名为 headerName 的公共成员,它定义了要使用的响应头的名称。

为了满足 ResponseClassifierConfig ,我们还必须实现一个名为 mk() 的方法,构建响应分类器。

配置初始化器

配置初始化器是一个特殊的类,linkerd 在启动时加载,告知 linkerd 关于它可以使用的配置类。我们创建一个名为 HeaderClassifierInitializer.java 的配置初始化器。这个类必须定义一个 configId 和一个 configClass。 当 linkerd 解析具有 kind 属性的配置块时,它将使用该configId查找配置初始化器,并尝试将该块反序列化为 configClass 的实例。 HeaderClassifierInitializer 告诉 linkerd 当它找到一个具有io.buoyant.headerClassifierresponseClassifier 块时,它应该将该块反序列化为 HeaderClassifierConfig

最后,为了使链接器能够在启动时动态加载配置初始化器,我们必须向服务装载器注册它。为此,只需创建一个名为 META-INF/services/io.buoyant.linkerd.ResponseClassifierConfig 的资源文件,并将配置初始化器的完全限定类名添加到该文件。

构建和打包

我们使用sbt构建我们的插件和assembly sbt插件,将其打包到一个jar。这是项目的build.sbt文件。注意,我们可以将任何 linkerd 依赖标记为“provided”。 这意味着这些依赖关系将由 linkerd 提供,不需要包含在 plugin jar中。类似地,linkerd 将提供scala标准库,因此我们可以通过设置 includeScala = false 来从jar中排除这些。

构建插件jar,运行

  1. ./sbt headerClassifier/assembly

安装

要在 linkerd 中安装此插件,只需将插件 jar 移动到 linkerd 的插件目录($L5D_HOME/plugins)中即可。 然后在你的 linkerd 配置中的 router 上添加一个 classifier 块:

  1. routers:
  2. - ...
  3. responseClassifier:
  4. kind: io.buoyant.headerClassifier
  5. headerName: status

如果使用 -log.level=DEBUG 运行 linkerd,那么应该在启动时看到打印的这行,表示 HeaderClassifierInitializer 已经被加载:

  1. LoadService: loaded instance of class io.buoyant.http.classifiers.HeaderClassifierInitializer for requested service io.buoyant.linkerd.ResponseClassifierInitializer

试试看

现在 plugins 目录中有我们的插件,我们来试试看。用这个简单的发送所有请求到localhost:8888 的配置启动 linkerd。

  1. routers:
  2. - protocol: http
  3. dtab: /svc/* => /$/inet/localhost/8888
  4. responseClassifier:
  5. kind: io.buoyant.headerClassifier
  6. headerName: status
  7. servers:
  8. - ip: 0.0.0.0
  9. port: 4140

然后,我们将在端口 8888 上启动一个简单的服务器,响应中的状态头指示成功:

  1. while true; do echo -e "HTTP/1.1 200 OK\r\nstatus: success\r\n" | nc -i 1 -l 8888; done

现在我们来发一个请求:

  1. curl -v localhost:4140

通过检查 linkerd 的 metrics,我们可以看到这个请求被归类为成功:

  1. curl -s localhost:9990/admin/metrics.json?pretty=1 | grep -E 'srv.*(success|failure)'
  2. "rt/http/srv/0.0.0.0/4140/success" : 1,

现在让我们重新启动服务器,并将它修改为设置头为“failure”:

  1. while true; do echo -e "HTTP/1.1 200 OK\r\nstatus: failure\r\n" | nc -i 1 -l 8888; done

并发出另一个请求:

  1. curl -v localhost:4140

现在,当我们检查 linkerd 的 metrics 时,我们会看到这个请求被归类为失败:

  1. curl -s localhost:9990/admin/metrics.json?pretty=1 | grep -E 'srv.*(success|failure)'
  2. "rt/http/srv/0.0.0.0/4140/failures" : 1,
  3. "rt/http/srv/0.0.0.0/4140/failures/com.twitter.finagle.service.ResponseClassificationSyntheticException" : 1,
  4. "rt/http/srv/0.0.0.0/4140/success" : 1,

更多信息

如果您对使用或开发 linkerd 插件有任何疑问或想分享您创建的内容,请转到 linkerd public Slack中。我们期待与您相逢!