微服务

Vert.x 服务发现

已弃用

此组件提供了发布和发现各种资源的基础设施,例如服务代理、HTTP 端点、数据源等。这些资源被称为服务服务是可发现的功能。它可以通过其类型、元数据和位置进行限定。因此,服务可以是一个数据库、一个服务代理、一个 HTTP 端点以及任何其他你可以想象到的资源,只要你能描述它、发现它并与它交互。它不一定是 Vert.x 实体,可以是任何东西。每个服务都由一个Record描述。

服务发现实现了面向服务计算中定义的交互。并在某种程度上,也提供了动态的面向服务计算交互。因此,应用程序可以对服务的到来和离开做出反应。

服务提供者可以:

  • 发布服务记录

  • 取消发布已发布的记录

  • 更新已发布服务的状态(停止、停止服务等)

服务消费者可以:

  • 查找服务

  • 绑定到选定的服务(获取一个ServiceReference)并使用它

  • 当消费者使用完服务后释放服务

  • 监听服务的到来、离开和修改。

消费者将:1) 查找符合其需求的服务记录,2) 检索提供服务访问权限的ServiceReference,3) 获取一个服务对象以访问服务,4) 完成后释放服务对象。

如果知道服务对象的类型(HTTP 客户端等),可以使用服务类型简化此过程,从而直接检索服务对象。

如上所述,提供者和消费者共享的核心信息是记录

提供者和消费者必须创建自己的ServiceDiscovery实例。这些实例在后台(分布式结构)协同工作,以保持服务集合的同步。

服务发现支持桥接,用于从/向其他发现技术导入和导出服务。

使用服务发现

要使用 Vert.x 服务发现,请将以下依赖项添加到构建描述符的dependencies部分。

  • Maven(在您的 pom.xml 中)

<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-service-discovery</artifactId>
<version>5.0.1</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

compile 'io.vertx:vertx-service-discovery:5.0.1'

总体概念

发现机制基于本节中解释的几个概念。

服务记录

服务记录是一个描述服务提供者发布的服务对象。它包含一个名称、一些元数据和一个位置对象(描述服务在哪里)。此记录是提供者(已发布它)和消费者(在查找时检索它)共享的唯一对象。

元数据乃至位置格式都取决于服务类型(见下文)。

当提供者准备好使用时,会发布记录;当服务提供者停止时,会撤回记录。

服务提供者和发布者

服务提供者是提供服务的实体。发布者负责发布描述提供者的记录。它可能是一个单一实体(提供者自行发布)或一个不同的实体。

服务消费者

服务消费者在服务发现中搜索服务。每次查找检索0..n记录。从这些记录中,消费者可以检索一个ServiceReference,它表示消费者和提供者之间的绑定。此引用允许消费者检索服务对象(以使用服务)并释放服务。

释放服务引用以清理对象和更新服务用法非常重要。

服务对象

服务对象是提供服务访问权限的对象。它可以有多种形式,例如代理、客户端,甚至对于某些服务类型可能不存在。服务对象的性质取决于服务类型。

请注意,由于 Vert.x 的多语言特性,如果您从 Java、Groovy 或其他语言检索服务对象,服务对象可能会有所不同。

服务类型

服务只是资源,而且有许多不同类型的服务。它们可以是功能服务、数据库、REST API 等。Vert.x 服务发现具有服务类型的概念来处理这种异构性。每种类型定义:

  • 服务如何定位(URI、事件总线地址、IP / DNS 等)—— 位置

  • 服务对象的性质(服务代理、HTTP 客户端、消息消费者等)—— 客户端

某些服务类型由服务发现组件实现和提供,但您可以添加自己的服务类型。

服务事件

每当服务提供者发布或撤回时,事件总线上都会触发一个事件。此事件包含已修改的记录。

此外,为了跟踪谁在使用谁,每当通过getReference检索或通过release释放引用时,事件总线上会发出事件以跟踪服务使用情况。

以下是有关这些事件的更多详细信息。

后端

服务发现使用 Vert.x 分布式数据结构来存储记录。因此,集群的所有成员都可以访问所有记录。这是默认的后端实现。您可以通过实现ServiceDiscoveryBackend SPI 来实现自己的后端。例如,我们提供了一个基于 Redis 的实现。

请注意,发现不需要 Vert.x 集群。在单节点模式下,结构是本地的。它可以通过ServiceImporter填充。从 3.5.0 开始,您可以通过将系统属性vertx-service-discovery-backend-local设置为true(或将环境变量VERTX-SERVICE-DISCOVERY-BACKEND-LOCAL设置为true)来在集群模式下使用本地结构。

创建服务发现实例

发布者和消费者必须创建自己的ServiceDiscovery实例才能使用发现基础设施。

ServiceDiscovery discovery = ServiceDiscovery.create(vertx);

// Customize the configuration
discovery = ServiceDiscovery.create(vertx,
    new ServiceDiscoveryOptions()
        .setAnnounceAddress("service-announce")
        .setName("my-name"));

// Do something...

discovery.close();

默认情况下,通告地址(发送服务事件的事件总线地址)是:vertx.discovery.announce。您还可以配置一个用于服务使用的名称(请参阅服务使用部分)。

当您不再需要服务发现对象时,请不要忘记将其关闭。它会关闭您配置的不同发现导入器和导出器,并释放服务引用。

您应该避免共享服务发现实例,以便服务使用能够代表正确的“使用情况”。

发布服务

一旦有了服务发现实例,就可以发布服务。过程如下:

  1. 为特定的服务提供者创建记录

  2. 发布此记录

  3. 保留已发布的记录,用于取消发布或修改服务。

要创建记录,可以使用Record类,或使用服务类型中的便捷方法。

Record record = new Record()
    .setType("eventbus-service-proxy")
    .setLocation(new JsonObject().put("endpoint", "the-service-address"))
    .setName("my-service")
    .setMetadata(new JsonObject().put("some-label", "some-value"));

discovery.publish(record).onComplete(ar -> {
  if (ar.succeeded()) {
    // publication succeeded
    Record publishedRecord = ar.result();
  } else {
    // publication failed
  }
});

// Record creation from a type
record = HttpEndpoint.createRecord("some-rest-api", "localhost", 8080, "/api");
discovery.publish(record).onComplete(ar -> {
  if (ar.succeeded()) {
    // publication succeeded
    Record publishedRecord = ar.result();
  } else {
    // publication failed
  }
});

保留对返回记录的引用很重要,因为此记录已通过registration id进行了扩展。

撤回服务

要撤回(取消发布)记录,请使用:

discovery.unpublish(record.getRegistration()).onComplete(ar -> {
  if (ar.succeeded()) {
    // Ok
  } else {
    // cannot un-publish the service, may have already been removed, or the record is not published
  }
});

查找服务

本节解释了检索服务的低级过程,每种服务类型都提供了便捷方法来聚合不同的步骤。

在消费者端,首先要做的就是查找记录。您可以搜索单个记录或所有匹配的记录。在第一种情况下,返回第一个匹配的记录。

消费者可以传递一个过滤器来选择服务。描述过滤器有两种方式:

  1. 一个函数,以Record作为参数并返回布尔值(它是一个谓词)

  2. 此过滤器是一个 JSON 对象。给定过滤器的每个条目都会与记录进行检查。所有条目必须与记录完全匹配。条目可以使用特殊值*来表示对键的要求,但不对值进行要求。

让我们看一个 JSON 过滤器的例子:

{ "name" = "a" } => matches records with name set to "a"
{ "color" = "*" } => matches records with "color" set
{ "color" = "red" } => only matches records with "color" set to "red"
{ "color" = "red", "name" = "a"} => only matches records with name set to "a", and color set to "red"

如果未设置 JSON 过滤器(null或为空),它将接受所有记录。使用函数时,要接受所有记录,无论记录如何,都必须返回true

以下是一些示例:

discovery.getRecord(r -> true).onComplete(ar -> {
  if (ar.succeeded()) {
    if (ar.result() != null) {
      // we have a record
    } else {
      // the lookup succeeded, but no matching service
    }
  } else {
    // lookup failed
  }
});

discovery.getRecord((JsonObject) null).onComplete(ar -> {
  if (ar.succeeded()) {
    if (ar.result() != null) {
      // we have a record
    } else {
      // the lookup succeeded, but no matching service
    }
  } else {
    // lookup failed
  }
});


// Get a record by name
discovery.getRecord(r -> r.getName().equals("some-name")).onComplete(ar -> {
  if (ar.succeeded()) {
    if (ar.result() != null) {
      // we have a record
    } else {
      // the lookup succeeded, but no matching service
    }
  } else {
    // lookup failed
  }
});

discovery.getRecord(new JsonObject().put("name", "some-service")).onComplete(ar -> {
  if (ar.succeeded()) {
    if (ar.result() != null) {
      // we have a record
    } else {
      // the lookup succeeded, but no matching service
    }
  } else {
    // lookup failed
  }
});

// Get all records matching the filter
discovery.getRecords(r -> "some-value".equals(r.getMetadata().getString("some-label"))).onComplete(ar -> {
  if (ar.succeeded()) {
    List<Record> results = ar.result();
    // If the list is not empty, we have matching record
    // Else, the lookup succeeded, but no matching service
  } else {
    // lookup failed
  }
});


discovery.getRecords(new JsonObject().put("some-label", "some-value")).onComplete(ar -> {
  if (ar.succeeded()) {
    List<Record> results = ar.result();
    // If the list is not empty, we have matching record
    // Else, the lookup succeeded, but no matching service
  } else {
    // lookup failed
  }
});

您可以使用getRecords检索单个记录或所有匹配的记录。默认情况下,记录查找仅包括status设置为UP的记录。这可以被覆盖:

  • 使用 JSON 过滤器时,只需将status设置为所需的值(或*以接受所有状态)。

  • 使用函数时,将getRecords中的includeOutOfService参数设置为true

检索服务引用

选择Record后,可以检索ServiceReference,然后检索服务对象:

ServiceReference reference1 = discovery.getReference(record1);
ServiceReference reference2 = discovery.getReference(record2);

// Then, gets the service object, the returned type depends on the service type:
// For http endpoint:
HttpClient client = reference1.getAs(HttpClient.class);
// For message source
MessageConsumer consumer = reference2.getAs(MessageConsumer.class);

// When done with the service
reference1.release();
reference2.release();

完成后不要忘记释放引用。

服务引用表示与服务提供者的绑定。

检索服务引用时,您可以传递一个JsonObject,用于配置服务对象。它可能包含有关服务对象的各种数据。某些服务类型不需要额外配置,有些则需要配置(如数据源)。

ServiceReference reference = discovery.getReferenceWithConfiguration(record, conf);

// Then, gets the service object, the returned type depends on the service type:
// For http endpoint:
HttpClient client = reference.getAs(HttpClient.class);

// Do something with the client...

// When done with the service
reference.release();

在前面的示例中,代码使用了getAs。参数是您期望获取的对象类型。如果您使用的是 Java,可以使用get。但在其他语言中,您必须传递预期的类型。

服务类型

如上所述,服务发现具有服务类型概念,以管理不同服务的异构性。

默认提供以下类型:

  • HttpEndpoint - 用于 REST API,服务对象是配置在主机和端口上的HttpClient(位置是 URL)。

  • EventBusService - 用于服务代理,服务对象是一个代理。其类型是代理接口(位置是地址)。

  • MessageSource - 用于消息源(发布者),服务对象是MessageConsumer(位置是地址)。

  • RedisDataSource - 用于 Redis 数据源,服务对象是Redis(客户端配置由位置、元数据和消费者配置计算得出)。

  • MongoDataSource - 用于 Mongo 数据源,服务对象是MongoClient(客户端配置由位置、元数据和消费者配置计算得出)。

本节详细介绍了服务类型,并描述了如何使用默认的服务类型。

无类型的服务

某些记录可能没有类型(ServiceType.UNKNOWN)。无法为这些记录检索引用,但可以从Recordlocationmetadata构建连接详细信息。

使用这些服务不会触发服务使用事件。

实现自己的服务类型

您可以通过实现ServiceType SPI 来创建自己的服务类型

  1. (可选)创建一个扩展ServiceType的公共接口。此接口仅用于提供辅助方法,以简化您的类型使用,例如createRecord方法、getX(其中X是您检索的服务对象类型)等等。请参阅HttpEndpointMessageSource作为示例。

  2. 创建一个实现ServiceType或您在步骤 1 中创建的接口的类。该类型有一个name,以及一个为该类型创建ServiceReference的方法。名称必须与与您的类型关联的Recordtype字段匹配。

  3. 创建一个扩展io.vertx.ext.discovery.types.AbstractServiceReference的类。您可以使用要返回的服务对象类型对类进行参数化。您必须实现AbstractServiceReference#retrieve()来创建服务对象。此方法只会被调用一次。如果您的服务对象需要清理,还要重写AbstractServiceReference#onClose()

  4. 创建一个打包在您的 jar 中的META-INF/services/io.vertx.servicediscovery.spi.ServiceType文件。在此文件中,只需指示在步骤 2 中创建的类的完全限定名称。

  5. 创建一个包含服务类型接口(步骤 1)、实现(步骤 2 和 3)和服务描述符文件(步骤 4)的 jar 包。将此 jar 放在应用程序的类路径中。这样,您的服务类型就可用了。

HTTP 端点

HTTP 端点表示一个 REST API 或一个使用 HTTP 请求可访问的服务。HTTP 端点服务对象是配置了主机、端口和 SSL 的HttpClient

发布 HTTP 端点

要发布 HTTP 端点,您需要一个Record。您可以使用HttpEndpoint.createRecord创建记录。

以下代码片段演示了如何从HttpEndpoint创建Record

Record record1 = HttpEndpoint.createRecord(
  "some-http-service", // The service name
  "localhost", // The host
  8433, // the port
  "/api" // the root of the service
);

discovery.publish(record1).onComplete(ar -> {
  // ...
});

Record record2 = HttpEndpoint.createRecord(
  "some-other-name", // the service name
  true, // whether or not the service requires HTTPs
  "localhost", // The host
  8433, // the port
  "/api", // the root of the service
  new JsonObject().put("some-metadata", "some value")
);

当您在容器或云上运行服务时,它可能不知道其公共 IP 和公共端口,因此发布必须由另一个拥有此信息的实体完成。通常是一个桥接。

消费 HTTP 端点

HTTP 端点发布后,消费者可以检索它。服务对象是一个配置了端口和主机的HttpClient

discovery.getRecord(new JsonObject().put("name", "some-http-service")).onComplete(ar1 -> {
  if (ar1.succeeded() && ar1.result() != null) {
    // Retrieve the service reference
    ServiceReference reference = discovery.getReference(ar1.result());
    // Retrieve the service object
    HttpClient client = reference.getAs(HttpClient.class);

    // You need to path the complete path
    client.request(HttpMethod.GET, "/api/persons").compose(request ->
      request
        .send()
        .compose(HttpClientResponse::body))
      .onComplete(ar2 -> {
      // Dont' forget to release the service
      reference.release();
    });
  }
});

您还可以使用HttpEndpoint.getClient方法将查找和服务检索合并为一次调用。

HttpEndpoint.getClient(discovery, new JsonObject().put("name", "some-http-service")).onComplete(ar -> {
  if (ar.succeeded()) {
    HttpClient client = ar.result();

    // You need to path the complete path
    client.request(HttpMethod.GET, "/api/persons").compose(request ->
      request
        .send()
        .compose(HttpClientResponse::body))
      .onComplete(ar2 -> {
        // Dont' forget to release the service
        ServiceDiscovery.releaseServiceObject(discovery, client);
      });
  }
});

在第二个版本中,服务对象使用ServiceDiscovery.releaseServiceObject释放,因此您无需保留服务引用。

自 Vert.x 3.4.0 以来,提供了另一个客户端。这个更高级别的客户端,名为WebClient,通常更容易使用。您可以使用以下方式检索WebClient实例:

discovery.getRecord(new JsonObject().put("name", "some-http-service")).onComplete(ar -> {
  if (ar.succeeded() && ar.result() != null) {
    // Retrieve the service reference
    ServiceReference reference = discovery.getReference(ar.result());
    // Retrieve the service object
    WebClient client = reference.getAs(WebClient.class);

    // You need to path the complete path
    client.get("/api/persons").send().onComplete(
      response -> {

        // ...

        // Dont' forget to release the service
        reference.release();

      });
  }
});

如果您更喜欢使用服务类型的方法:

HttpEndpoint.getWebClient(discovery, new JsonObject().put("name", "some-http-service")).onComplete(ar -> {
  if (ar.succeeded()) {
    WebClient client = ar.result();

    // You need to path the complete path
    client.get("/api/persons")
      .send().onComplete(response -> {

        // ...

        // Dont' forget to release the service
        ServiceDiscovery.releaseServiceObject(discovery, client);

      });
  }
});

事件总线服务

事件总线服务是服务代理。它们在事件总线之上实现了异步 RPC 服务。从事件总线服务检索服务对象时,您会获得正确类型的服务代理。您可以从EventBusService访问辅助方法。

请注意,服务代理(服务实现和服务接口)是在 Java 中开发的。

发布事件总线服务

要发布事件总线服务,您需要创建一个Record

Record record = EventBusService.createRecord(
    "some-eventbus-service", // The service name
    "address", // the service address,
    "examples.MyService", // the service interface as string
    new JsonObject()
        .put("some-metadata", "some value")
);

discovery.publish(record).onComplete(ar -> {
  // ...
});

您也可以将服务接口作为类传递。

Record record = EventBusService.createRecord(
"some-eventbus-service", // The service name
"address", // the service address,
MyService.class // the service interface
);

discovery.publish(record).onComplete(ar -> {
// ...
});

消费事件总线服务

要消费事件总线服务,您可以检索记录然后获取引用,或者使用EventBusService接口,它将这两个操作合并为一次调用。

使用第一种方法,代码如下:

discovery.getRecord(new JsonObject().put("name", "some-eventbus-service")).onComplete(ar -> {
if (ar.succeeded() && ar.result() != null) {
// Retrieve the service reference
ServiceReference reference = discovery.getReference(ar.result());
// Retrieve the service object
MyService service = reference.getAs(MyService.class);

// Dont' forget to release the service
reference.release();
}
});

使用EventBusService类时,您可以按如下方式获取代理:

EventBusService.getProxy(discovery, MyService.class).onComplete(ar -> {
if (ar.succeeded()) {
MyService service = ar.result();

// Dont' forget to release the service
ServiceDiscovery.releaseServiceObject(discovery, service);
}
});

消息源

消息源是向事件总线上特定地址发送消息的组件。消息源客户端是MessageConsumer

消息源服务的location是发送消息的事件总线地址。

发布消息源

与其他服务类型一样,发布消息源是一个两步过程:

  1. 使用MessageSource创建记录

  2. 发布记录

Record record = MessageSource.createRecord(
    "some-message-source-service", // The service name
    "some-address" // The event bus address
);

discovery.publish(record).onComplete(ar -> {
  // ...
});

record = MessageSource.createRecord(
    "some-other-message-source-service", // The service name
    "some-address", // The event bus address
    "examples.MyData" // The payload type
);

在第二条记录中,也指明了有效负载的类型。此信息是可选的。

在 Java 中,您可以使用Class参数。

Record record1 = MessageSource.createRecord(
"some-message-source-service", // The service name
"some-address", // The event bus address
JsonObject.class // The message payload type
);

Record record2 = MessageSource.createRecord(
"some-other-message-source-service", // The service name
"some-address", // The event bus address
JsonObject.class, // The message payload type
new JsonObject().put("some-metadata", "some value")
);

消费消息源

在消费者端,您可以检索记录和引用,或者使用MessageSource类一次性检索服务。

使用第一种方法,代码如下:

discovery.getRecord(new JsonObject().put("name", "some-message-source-service")).onComplete(ar -> {
  if (ar.succeeded() && ar.result() != null) {
    // Retrieve the service reference
    ServiceReference reference = discovery.getReference(ar.result());
    // Retrieve the service object
    MessageConsumer<JsonObject> consumer = reference.getAs(MessageConsumer.class);

    // Attach a message handler on it
    consumer.handler(message -> {
      // message handler
      JsonObject payload = message.body();
    });
  }
});

当使用MessageSource时,它变为:

MessageSource.<JsonObject>getConsumer(discovery, new JsonObject().put("name", "some-message-source-service")).onComplete(ar -> {
  if (ar.succeeded()) {
    MessageConsumer<JsonObject> consumer = ar.result();

    // Attach a message handler on it
    consumer.handler(message -> {
      // message handler
      JsonObject payload = message.body();
    });
    // ...
  }
});

Redis 数据源

Redis 数据源是 Redis 持久化数据库的专用化。Redis 数据源服务的客户端是Redis

发布 Redis 服务

发布 Redis 数据源是一个两步过程:

  1. 使用RedisDataSource创建记录

  2. 发布记录

Record record = RedisDataSource.createRecord(
  "some-redis-data-source-service", // The service name
  new JsonObject().put("url", "localhost"), // The location
  new JsonObject().put("some-metadata", "some-value") // Some metadata
);

discovery.publish(record).onComplete(ar -> {
  // ...
});

location是一个简单的 JSON 对象,它应该提供访问 Redis 数据源的字段(URL、端口等)。

消费 Redis 服务

如前一节所述,访问数据源的方式取决于数据源本身。要构建Redis,您可以合并配置:记录位置、元数据和消费者提供的 JSON 对象。

discovery.getRecord(
  new JsonObject().put("name", "some-redis-data-source-service")).onComplete(ar -> {
    if (ar.succeeded() && ar.result() != null) {
      // Retrieve the service reference
      ServiceReference reference = discovery.getReference(ar.result());

      // Retrieve the service instance
      Redis client = reference.getAs(Redis.class);

      // ...

      // when done
      reference.release();
    }
  });

您还可以使用RedisDataSource类一次性完成查找和检索。

RedisDataSource.getRedisClient(discovery,
  new JsonObject().put("name", "some-redis-data-source-service")).onComplete(ar -> {
    if (ar.succeeded()) {
      Redis client = ar.result();

      // ...

      // Dont' forget to release the service
      ServiceDiscovery.releaseServiceObject(discovery, client);

    }
  });

Mongo 数据源

Mongo 数据源是 MongoDB 数据库的专用化。Mongo 数据源服务的客户端是MongoClient

发布 Mongo 服务

发布 Mongo 数据源是一个两步过程:

  1. 使用MongoDataSource创建记录

  2. 发布记录

Record record = MongoDataSource.createRecord(
  "some-data-source-service", // The service name
  new JsonObject().put("connection_string", "some mongo connection"), // The location
  new JsonObject().put("some-metadata", "some-value") // Some metadata
);

discovery.publish(record).onComplete(ar -> {
  // ...
});

location是一个简单的 JSON 对象,它应该提供访问 Mongo 数据源的字段(URL、端口等)。

消费 Mongo 服务

如前一节所述,访问数据源的方式取决于数据源本身。要构建MongoClient,您可以合并配置:记录位置、元数据和消费者提供的 JSON 对象。

discovery.getRecord(
  new JsonObject().put("name", "some-data-source-service")).onComplete(ar -> {
    if (ar.succeeded() && ar.result() != null) {
      // Retrieve the service reference
      ServiceReference reference = discovery.getReferenceWithConfiguration(
        ar.result(), // The record
        new JsonObject().put("username", "clement").put("password", "*****")); // Some additional metadata

      // Retrieve the service object
      MongoClient client = reference.get();

      // ...

      // when done
      reference.release();
    }
  });

您还可以使用MongoDataSource类一次性完成查找和检索。

MongoDataSource.getMongoClient(discovery,
  new JsonObject().put("name", "some-data-source-service"),
  new JsonObject().put("username", "clement").put("password", "*****") // Some additional metadata
).onComplete(ar -> {
    if (ar.succeeded()) {
      MongoClient client = ar.result();

      // ...

      // Dont' forget to release the service
      ServiceDiscovery.releaseServiceObject(discovery, client);

    }
  });

监听服务到达和离开

每当发布或移除提供者时,都会在vertx.discovery.announce地址上发布一个事件。此地址可以通过ServiceDiscoveryOptions配置。

收到的记录有一个status字段,指示记录的新状态:

  • UP:服务可用,您可以开始使用它

  • DOWN:服务不再可用,您不应再使用它

  • OUT_OF_SERVICE:服务未运行,您不应再使用它,但它可能会稍后恢复。

监听服务使用情况

每次检索(bind)或释放(release)服务引用时,都会在vertx.discovery.usage地址上发布一个事件。此地址可以通过ServiceDiscoveryOptions配置。

它允许您监听服务使用情况并映射服务绑定。

收到的消息是一个JsonObject,包含:

  • record字段中的记录

  • type字段中的事件类型。它要么是bind,要么是release

  • id字段中的服务发现 ID(其名称或节点 ID)

id可以通过ServiceDiscoveryOptions配置。默认情况下,在单节点配置中是“localhost”,在集群模式下是节点的 ID。

您可以通过使用setUsageAddress将使用地址设置为null来禁用服务使用支持。

服务发现桥接

桥接允许您从/向其他发现机制(如 Docker、Kubernetes、Consul 等)导入和导出服务。每个桥接决定了服务如何导入和导出。它不一定是双向的。

您可以通过实现ServiceImporter接口并使用registerServiceImporter注册它来提供您自己的桥接。

第二个参数可以为桥接提供一个可选配置。

当桥接注册时,会调用start方法。它允许您配置桥接。当桥接配置好,准备就绪并导入/导出了初始服务后,它必须完成给定的Future。如果桥接的 start 方法是阻塞的,它必须使用executeBlocking构造,并完成给定的 future 对象。

当服务发现停止时,桥接也会停止。调用close方法,提供清理资源、移除已导入/导出服务的机会。此方法必须完成给定的Future以通知调用者完成。

请注意,在集群中,只有一个成员需要注册桥接,因为所有成员都可以访问记录。

附加桥接

除了此库支持的桥接之外,Vert.x 服务发现还提供了您可以在应用程序中使用的附加桥接。

Consul 桥接

此发现桥接将服务从Consul导入到 Vert.x 服务发现中。该桥接连接到 Consul 代理(服务器)并定期扫描服务:

  • 导入新服务

  • 维护模式下的服务或已从 Consul 中移除的服务将被移除

此桥接使用 Consul 的 HTTP API。它不导出到 Consul,也不支持服务修改。

服务类型从tags中推断出来。如果某个tag与已知服务类型匹配,则将使用此服务类型。如果不匹配,则将服务导入为unknown。目前仅支持http-endpoint

使用桥接

要使用此 Vert.x 发现桥接,请将以下依赖项添加到构建描述符的dependencies部分。

  • Maven(在您的 pom.xml 中)

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-service-discovery-bridge-consul</artifactId>
  <version>5.0.1</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

compile 'io.vertx:vertx-service-discovery-bridge-consul:5.0.1'

然后,在创建服务发现时,按如下方式注册此桥接:

ServiceDiscovery.create(vertx)
    .registerServiceImporter(new ConsulServiceImporter(),
        new JsonObject()
            .put("host", "localhost")
            .put("port", 8500)
            .put("scan-period", 2000));

您可以配置:

  • 代理主机,使用host属性,默认为localhost

  • 代理端口,使用port属性,默认为 8500。

  • ACL 令牌,使用acl_token属性,默认为 null。

  • 扫描周期,使用scan-period属性。时间以毫秒为单位,默认为 2000 毫秒。

Kubernetes 桥接

此发现桥接将服务从 Kubernetes(或 Openshift v3)导入到 Vert.x 服务发现中。Kubernetes 服务被映射到Record。此桥接仅支持将服务从 Kubernetes 导入到 Vert.x 中(反之则不支持)。

Record是从 Kubernetes Service 创建的。服务类型从service-type标签或服务暴露的端口中推断出来。

使用桥接

要使用此 Vert.x 发现桥接,请将以下依赖项添加到构建描述符的dependencies部分。

  • Maven(在您的 pom.xml 中)

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-service-discovery-bridge-kubernetes</artifactId>
  <version>5.0.1</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

compile 'io.vertx:vertx-service-discovery-bridge-kubernetes:5.0.1'

然后,在创建服务发现时,按如下方式注册此桥接:

JsonObject defaultConf = new JsonObject();
serviceDiscovery.registerServiceImporter(new KubernetesServiceImporter(), defaultConf);

配置桥接

桥接配置使用:

  • OAuth 令牌(默认使用/var/run/secrets/kubernetes.io/serviceaccount/token的内容)

  • 搜索服务的命名空间(默认为default)。

请注意,应用程序必须能够访问 Kubernetes 并能够读取所选的命名空间。

服务到记录的映射

记录创建如下:

  • 服务类型从service.type标签推断。如果未设置此标签,服务类型将设置为unknown

  • 记录的名称是服务的名称。

  • 服务的标签被映射到元数据。

  • 此外还添加了:kubernetes.uuidkubernetes.namespacekubernetes.name

  • 位置从服务的*第一个**端口推断。

对于 HTTP 端点,如果服务将ssl标签设置为true,则sslhttps)属性将设置为true

动态性

桥接在start时导入所有服务,并在stop时移除它们。在此期间,它会监视 Kubernetes 服务,并添加新的服务,移除已删除的服务。

支持的类型

桥接使用service-type标签来推断类型。此外,它还会检查服务的端口。支持的类型包括:

  • 端口 80、443 以及 8080 到 9000:HTTP 端点

  • 端口 5432 和 5433:JDBC 数据源 (PostgreSQL)

  • 端口 3306 和 13306:JDBC 数据源 (MySQL)

  • 端口 6379:Redis 数据源

  • 端口 27017、27018 和 27019:MongoDB 数据源

如果存在,service-type将覆盖基于端口的推断。

Zookeeper 桥接

此发现桥接将服务从Apache Zookeeper导入到 Vert.x 服务发现中。该桥接使用Curator 扩展用于服务发现

服务描述作为 JSON 对象读取(合并到 Vert.x 服务记录元数据中)。服务类型通过读取service-type从该描述中推断出来。

使用桥接

要使用此 Vert.x 发现桥接,请将以下依赖项添加到构建描述符的dependencies部分。

  • Maven(在您的 pom.xml 中)

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-service-discovery-bridge-zookeeper</artifactId>
  <version>5.0.1</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

compile 'io.vertx:vertx-service-discovery-bridge-zookeeper:5.0.1'

然后,在创建服务发现时,按如下方式注册此桥接:

ServiceDiscovery.create(vertx)
    .registerServiceImporter(new ZookeeperServiceImporter(),
        new JsonObject()
            .put("connection", "127.0.0.1:2181"));

只有connection配置是强制性的。它是 Zookeeper 服务器的连接字符串

此外,您可以配置:

  • maxRetries:连接尝试次数,默认为 3。

  • baseSleepTimeBetweenRetries:两次重试之间等待的毫秒数(指数退避策略)。默认为 1000 毫秒。

  • basePath:服务存储在 Zookeeper 中的路径。默认为/discovery

  • connectionTimeoutMs:连接超时(毫秒)。默认为 1000。

  • canBeReadOnly:后端是否支持只读模式(默认为 true)。

ServiceDiscovery.create(vertx)
    .registerServiceImporter(new ZookeeperServiceImporter(),
        new JsonObject()
            .put("connection", "127.0.0.1:2181")
            .put("maxRetries", 5)
            .put("baseSleepTimeBetweenRetries", 2000)
            .put("basePath", "/services")
    );

此发现桥接将服务从 Docker Links 导入到 Vert.x 服务发现中。当您将一个 Docker 容器链接到另一个 Docker 容器时,Docker 会注入一组环境变量。此桥接分析这些环境变量,并为每个链接导入服务记录。服务类型从service.type标签推断。如果未设置,则服务将作为unknown导入。目前仅支持http-endpoint

由于链接是在容器启动时创建的,因此导入的记录在桥接启动时创建,之后不会改变。

使用桥接

要使用此 Vert.x 发现桥接,请将以下依赖项添加到构建描述符的dependencies部分。

  • Maven(在您的 pom.xml 中)

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-service-discovery-bridge-docker</artifactId>
  <version>5.0.1</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

compile 'io.vertx:vertx-service-discovery-bridge-docker:5.0.1'

然后,在创建服务发现时,按如下方式注册此桥接:

ServiceDiscovery.create(vertx)
    .registerServiceImporter(new DockerLinksServiceImporter(), new JsonObject());

该桥接不需要任何进一步的配置。

附加后端

除了此库支持的后端之外,Vert.x 服务发现还提供了您可以在应用程序中使用的附加后端。

Redis 后端

服务发现有一个可插拔的后端,使用ServiceDiscoveryBackend SPI。这是基于 Redis 的 SPI 实现。

使用 Redis 后端

要使用 Redis 后端,请将以下依赖项添加到构建描述符的dependencies部分。

  • Maven(在您的 pom.xml 中)

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-service-discovery-backend-redis</artifactId>
  <version>5.0.1</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

compile 'io.vertx:vertx-service-discovery-backend-redis:5.0.1'

请注意,您的classpath中只能有一个 SPI 实现。如果没有,则使用默认后端。

配置

后端基于vertx-redis-client。配置是客户端配置以及指示记录存储在 Redis 中哪个keykey

这是一个示例

ServiceDiscovery.create(vertx, new ServiceDiscoveryOptions()
    .setBackendConfiguration(
        new JsonObject()
            .put("connectionString", "redis://:6379")
            .put("key", "records")
    ));

重要的是要注意,后端配置是通过setBackendConfiguration方法(如果您使用 JSON,则通过backendConfiguration条目)传递的。

ServiceDiscovery.create(vertx,
  new ServiceDiscoveryOptions(new JsonObject()
    .put("backendConfiguration",
      new JsonObject().put("connectionString", "redis://:6379").put("key", "my-records")
)));