<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-service-discovery</artifactId>
<version>5.0.1</version>
</dependency>
Vert.x 服务发现
此组件提供了发布和发现各种资源的基础设施,例如服务代理、HTTP 端点、数据源等。这些资源被称为服务
。服务
是可发现的功能。它可以通过其类型、元数据和位置进行限定。因此,服务
可以是一个数据库、一个服务代理、一个 HTTP 端点以及任何其他你可以想象到的资源,只要你能描述它、发现它并与它交互。它不一定是 Vert.x 实体,可以是任何东西。每个服务都由一个Record
描述。
服务发现实现了面向服务计算中定义的交互。并在某种程度上,也提供了动态的面向服务计算交互。因此,应用程序可以对服务的到来和离开做出反应。
服务提供者可以:
-
发布服务记录
-
取消发布已发布的记录
-
更新已发布服务的状态(停止、停止服务等)
服务消费者可以:
-
查找服务
-
绑定到选定的服务(获取一个
ServiceReference
)并使用它 -
当消费者使用完服务后释放服务
-
监听服务的到来、离开和修改。
消费者将:1) 查找符合其需求的服务记录,2) 检索提供服务访问权限的ServiceReference
,3) 获取一个服务对象以访问服务,4) 完成后释放服务对象。
如果知道服务对象的类型(HTTP 客户端等),可以使用服务类型简化此过程,从而直接检索服务对象。
如上所述,提供者和消费者共享的核心信息是记录
。
提供者和消费者必须创建自己的ServiceDiscovery
实例。这些实例在后台(分布式结构)协同工作,以保持服务集合的同步。
服务发现支持桥接,用于从/向其他发现技术导入和导出服务。
使用服务发现
要使用 Vert.x 服务发现,请将以下依赖项添加到构建描述符的dependencies部分。
-
Maven(在您的
pom.xml
中)
-
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
。您还可以配置一个用于服务使用的名称(请参阅服务使用部分)。
当您不再需要服务发现对象时,请不要忘记将其关闭。它会关闭您配置的不同发现导入器和导出器,并释放服务引用。
您应该避免共享服务发现实例,以便服务使用能够代表正确的“使用情况”。
发布服务
一旦有了服务发现实例,就可以发布服务。过程如下:
-
为特定的服务提供者创建记录
-
发布此记录
-
保留已发布的记录,用于取消发布或修改服务。
要创建记录,可以使用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
}
});
查找服务
本节解释了检索服务的低级过程,每种服务类型都提供了便捷方法来聚合不同的步骤。
在消费者端,首先要做的就是查找记录。您可以搜索单个记录或所有匹配的记录。在第一种情况下,返回第一个匹配的记录。
消费者可以传递一个过滤器来选择服务。描述过滤器有两种方式:
-
一个函数,以
Record
作为参数并返回布尔值(它是一个谓词) -
此过滤器是一个 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();
服务类型
如上所述,服务发现具有服务类型概念,以管理不同服务的异构性。
默认提供以下类型:
-
HttpEndpoint
- 用于 REST API,服务对象是配置在主机和端口上的HttpClient
(位置是 URL)。 -
EventBusService
- 用于服务代理,服务对象是一个代理。其类型是代理接口(位置是地址)。 -
MessageSource
- 用于消息源(发布者),服务对象是MessageConsumer
(位置是地址)。 -
RedisDataSource
- 用于 Redis 数据源,服务对象是Redis
(客户端配置由位置、元数据和消费者配置计算得出)。 -
MongoDataSource
- 用于 Mongo 数据源,服务对象是MongoClient
(客户端配置由位置、元数据和消费者配置计算得出)。
本节详细介绍了服务类型,并描述了如何使用默认的服务类型。
无类型的服务
某些记录可能没有类型(ServiceType.UNKNOWN
)。无法为这些记录检索引用,但可以从Record
的location
和metadata
构建连接详细信息。
使用这些服务不会触发服务使用事件。
实现自己的服务类型
您可以通过实现ServiceType
SPI 来创建自己的服务类型
-
(可选)创建一个扩展
ServiceType
的公共接口。此接口仅用于提供辅助方法,以简化您的类型使用,例如createRecord
方法、getX
(其中X
是您检索的服务对象类型)等等。请参阅HttpEndpoint
或MessageSource
作为示例。 -
创建一个实现
ServiceType
或您在步骤 1 中创建的接口的类。该类型有一个name
,以及一个为该类型创建ServiceReference
的方法。名称必须与与您的类型关联的Record
的type
字段匹配。 -
创建一个扩展
io.vertx.ext.discovery.types.AbstractServiceReference
的类。您可以使用要返回的服务对象类型对类进行参数化。您必须实现AbstractServiceReference#retrieve()
来创建服务对象。此方法只会被调用一次。如果您的服务对象需要清理,还要重写AbstractServiceReference#onClose()
。 -
创建一个打包在您的 jar 中的
META-INF/services/io.vertx.servicediscovery.spi.ServiceType
文件。在此文件中,只需指示在步骤 2 中创建的类的完全限定名称。 -
创建一个包含服务类型接口(步骤 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
释放,因此您无需保留服务引用。
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是发送消息的事件总线地址。
发布消息源
与其他服务类型一样,发布消息源是一个两步过程:
-
使用
MessageSource
创建记录 -
发布记录
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 数据源是一个两步过程:
-
使用
RedisDataSource
创建记录 -
发布记录
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 数据源是一个两步过程:
-
使用
MongoDataSource
创建记录 -
发布记录
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 对象。
请注意,在集群中,只有一个成员需要注册桥接,因为所有成员都可以访问记录。
附加桥接
除了此库支持的桥接之外,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.uuid
、kubernetes.namespace
、kubernetes.name
。 -
位置从服务的*第一个**端口推断。
对于 HTTP 端点,如果服务将ssl
标签设置为true
,则ssl
(https
)属性将设置为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 桥接
此发现桥接将服务从 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 中哪个key的key
。
这是一个示例
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")
)));