Vert.x 提供了一个服务发现的基础组件,用来发布和发现各种类型的资源,比如服务代理、HTTP端点(endpoint)、数据源(data source)等等。
这些资源都可以称为服务。服务就是一个可以被发现和访问的功能,可以通过它的类型、元数据和位置来进行描述。
所以,服务可以是一个数据库、一个服务代理、一个HTTP应用,以及任何你能想到的可描述、可发现、可交互的资源。
它不一定是Vert.x实体,它可以是任何组件。在Vert.x 服务发现组件中,我们通过
Record
来描述每个服务。
服务发现组件实现了面向服务计算中定义的服务交互。此外,在某种程度上,还提供了动态的面向服务计算交互,这样应用程序可以对各种服务的上线、下线作出反应。
一个服务提供者可以:
发布一个服务记录
将已经发布的服务记录注销
更新已发布服务记录的状态(下线、服务暂停等等)
一个服务消费者可以:
查找各种服务
绑定到某个服务(它所获取到的 ServiceReference
) )并且使用这个服务
当使用完后,释放绑定的服务
监听服务的上线、下线和状态变更的消息
Consumer would 1) lookup a service record matching their need, 2) retrieve the
ServiceReference
that give access to the service, 3) get a service object to access
the service, 4) release the service object once done.
如果知道服务的类型(JDBC客户端、HTTP客户端),整个过程就可以简化为通过服务类型直接获取服务对象。
从上面可以看出,服务提供者和服务消费者,通过服务记录 ( Record
对象) 来共享关键的信息。
服务提供者和消费者,必须创建他们自己的 ServiceDiscovery
实例。这些实例通过底层的分布式数据结构来协同保持服务集合的同步。
服务发现组件支持桥接的方式,来从其他服务发现技术中导入和导出服务。
要使用Vert.x 服务发现组件,需要将下列依赖加入到依赖配置中文件:
Maven ( pom.xml
文件中):
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-service-discovery</artifactId>
<version>3.6.2</version>
</dependency>
Gradle ( build.gradle
文件中):
compile 'io.vertx:vertx-service-discovery:3.6.2'
本节将解释服务发现机制所涉及到的一些概念。
我们用服务记录( Record
对象)来描述服务提供者提供的服务,它包含了服务名称、一些元数据和一个描述服务所在位置的位置对象。
服务记录的元数据、甚至位置的格式,都有赖于 服务的类型
(详见后续章节)。
当服务提供者准备好可以提供服务时,会发布一条服务记录,在服务停止的时候,会收回这条服务记录。
服务提供者是提供服务的实体,而发布者的职责是发布服务记录,通过该服务记录来描述服务提供者的信息。服务提供者和发布者可以是同一个实体,也可以是不同的实体。
服务消费者在Service Discovery中搜索服务,每次搜索得到的结果是0..n条服务记录(
Record
)。通过这些服务记录,消费者可以获得服务引用(
ServiceReference
)。服务引用的作用是绑定服务消费者和服务提供者。通过服务引用,消费者可以得到服务对象来使用服务,也可以通过服务引用释放服务对象。
在使用完服务后,必须释放服务引用,才能清理服务对象和更新服务使用状态。
服务对象为服务消费者提供了一条获取服务的通道,它有各种实现方式,比如一个代理对象、一个客户端对象、甚至某些类型的服务可能不存在这样一个服务对象。服务对象的表现有赖于服务的类型。
由于Vert.x的多语言特性,当你从Java、Groovy或其他语言中获取服务对象的时候,可能会有差异。
服务就是资源。有很多各种各样的服务,比如功能性的服务组件、数据库、REST API等等。Vert.x 服务发现组件通过服务类型的概念来处理这种差异。每种服务类型都需要定义:
+ 如何定位服务(URI、Event Bus地址、IP/DNS 等) - location + 提供服务的对象的性质(服务代理、HTTP Client、消息消费者 等) - client
服务发现组件提供了一些现成的服务类型,但你也可以添加自己的服务类型。
每当发布或回收服务时,`Event Bus`中都会触发一个事件,这个事件包含着被修改的服务记录。
每当通过
getReference
方法获取一个服务引用或者通过
release
方法释放一个服务引用时,都会有事件发送到 Event Bus 中,用来跟踪服务的使用情况。
关于服务事件的更详细内容参考后续章节。
服务发现组件使用Vert.x的分布式数据结构来存储服务记录。所以,集群中所有的成员都可以访问到所有的服务记录,这是服务后端的默认实现。
你也可以实现自己的服务记录存储后端,只要实现 ServiceDiscoveryBackend
接口就可以了。
比如,Vert.x还通过实现该接口提供了基于Redis的存储后端。
注意服务发现模块并不需要运行在Vert.x 集群模式下。在单机模式下,服务记录存储于本地,并且可以通过 ServiceImporter
来导入。
从 3.5.0 版本开始,你甚至可以在集群模式下采用本地结构储存,通过设置 vertx-service-discovery-backend-local
为 true
(或者设置环境变量 VERTX-SERVICE-DISCOVERY-BACKEND-LOCAL
为 true
)
服务发布者和服务消费者都必须通过单独创建自己的 ServiceDiscovery
实例来使用服务发现模块:
<?php
use io\vertx\jphp\servicediscovery\ServiceDiscovery;
// Use default configuration
$discovery = ServiceDiscovery::create($vertx);
// Customize the configuration
$discovery = ServiceDiscovery::create($vertx, array(
"announceAddress" => "service-announce",
"name" => "my-name"
));
// Do something...
$discovery->close();
在默认情况下,服务事件发送到Event Bus中的地址是 vertx.discovery.announce
,你可以自己配置一个(查看服务使用章节)。
当你不再需要 ServiceDiscovery
对象时,不要忘记关掉它(通过 close
方法)。它会把你配置的不同的服务导入/导出模块都关掉,并且释放服务引用。
你应该禁止在实例中共享 ServiceDiscovery
对象。
有了 ServiceDiscovery
实例,就可以发布服务了。发布的流程如下:
为服务提供者创建一个服务记录
发布这个服务记录
保存这个发布记录的引用,后面可以用来取消发布或者修改发布
你可以通过 Record
类或者各种服务类型类提供的快捷方法来创建服务记录。
<?php
use io\vertx\jphp\servicediscovery\types\HttpEndpoint;
// Manual record creation
$record = array(
"type" => "eventbus-service-proxy",
"location" => array(
"endpoint" => "the-service-address"
),
"name" => "my-service",
"metadata" => array(
"some-label" => "some-value"
)
);
$discovery->publish($record, function ($ar, $ar_err) {
if ($ar != null) {
// publication succeeded
$publishedRecord = $ar;
} else {
// publication failed
};
});
// Record creation from a type
$record = HttpEndpoint::createRecord("some-rest-api", "localhost", 8080, "/api");
$discovery->publish($record, function ($ar, $ar_err) {
if ($ar != null) {
// publication succeeded
$publishedRecord = $ar;
} else {
// publication failed
};
});
一定要保持一个指向服务记录对象的引用,因为这个返回的服务记录会带有一个 注册ID。
要取消一个已发布的服务,可以用如下方式:
<?php
$discovery->unpublish($record["registration"], function ($ar, $ar_err) {
if ($ar != null) {
// Ok
} else {
// cannot un-publish the service, may have already been removed, or the record is not published
};
});
本节讲述的是最基本的获取服务的方法。每种服务类型接口,都提供了快捷的方法,来简化获取服务的步骤。
在服务消费端,第一步要做的事情就是查找服务记录。你可以查找并获取一条服务记录,也可以获取一批满足条件的记录。如果是获取一条记录,那么将返回第一条满足条件的服务记录。
服务消费者通过传递一个过滤器来选择服务,有两种形式的过滤器:
一个接收 Record
对象的函数,这个函数返回一个布尔值(就是一个 predicate
,即判断函数)
过滤器是一个JSON对象。对象中的每个条目,将会用来过滤服务记录。服务记录必须满足所有的条目要求。这些条目可以使用 *
号来代表必须存在某个key值,而不管value值
让我们看一些JSON过滤器的例子
{ "name" = "a" } => 匹配所有名称为"a"的记录 { "color" = "*" } => 匹配所有设置了 "color" 的记录 { "color" = "red" } => 匹配所有"color" 值为 "red"的记录 { "color" = "red", "name" = "a"} => 匹配所有名称为 "a", 并且"color"值为"red"的记录
如果JSON过滤器未设置(为空或 null
),获取时将获取到所有的服务记录。当使用函数形式时,要获取所有的服务记录,你只需要返回 true
而不需要管服务记录的内容。
下面是一些例子:
<?php
// Get any record
$discovery->getRecord(function ($r) {
true;
}, function ($ar, $ar_err) {
if ($ar != null) {
if ($ar != null) {
// we have a record
} else {
// the lookup succeeded, but no matching service
};
} else {
// lookup failed
};
});
$discovery->getRecord(null, function ($ar, $ar_err) {
if ($ar != null) {
if ($ar != null) {
// we have a record
} else {
// the lookup succeeded, but no matching service
};
} else {
// lookup failed
};
});
// Get a record by name
$discovery->getRecord(function ($r) {
$r["name"] == "some-name";
}, function ($ar, $ar_err) {
if ($ar != null) {
if ($ar != null) {
// we have a record
} else {
// the lookup succeeded, but no matching service
};
} else {
// lookup failed
};
});
$discovery->getRecord(array(
"name" => "some-service"
), function ($ar, $ar_err) {
if ($ar != null) {
if ($ar != null) {
// we have a record
} else {
// the lookup succeeded, but no matching service
};
} else {
// lookup failed
};
});
// Get all records matching the filter
$discovery->getRecords(function ($r) {
"some-value" == $r["metadata"]["some-label"];
}, function ($ar, $ar_err) {
if ($ar != null) {
$results = $ar;
// If the list is not empty, we have matching record
// Else, the lookup succeeded, but no matching service
} else {
// lookup failed
};
});
$discovery->getRecords(array(
"some-label" => "some-value"
), function ($ar, $ar_err) {
if ($ar != null) {
$results = $ar;
// If the list is not empty, we have matching record
// Else, the lookup succeeded, but no matching service
} else {
// lookup failed
};
});
你可以获取一条服务记录,也可以通过
getRecords
方法获取所有匹配到的服务记录。默认情况下,服务查找只会包含状态为`UP`的服务,可以通过如下方式覆盖默认设置:
当使用JSON过滤器,设置`status`属性为你想要的值(或者 *
来接收所有的状态)
当使用函数过滤器,将 getRecords
方法的参数`includeOutOfService`设置为`true`
当你选择好了服务记录( Record
对象)后,你就可以获得到一个
ServiceReference
,然后得到服务对象:
<?php
use io\vertx\jphp\core\http\HttpClient;
use io\vertx\jphp\core\eventbus\MessageConsumer;
$reference1 = $discovery->getReference($record1);
$reference2 = $discovery->getReference($record2);
// Then, gets the service object, the returned type depends on the service type:
// For http endpoint:
$client = $reference1->getAs(HttpClient::class);
// For message source
$consumer = $reference2->getAs(MessageConsumer::class);
// When done with the service
$reference1->release();
$reference2->release();
使用完后,不要忘记释放服务引用。
服务引用代表了一个绑定的服务提供者。
获取服务引用的时候,可以传递一个 JsonObject
对象来配置服务对象,可以包括用来配置服务对象的各种参数。某些服务类型不需要额外的配置,有些需要(比如数据库对象):
<?php
use io\vertx\jphp\ext\jdbc\JDBCClient;
$reference = $discovery->getReferenceWithConfiguration($record, $conf);
// Then, gets the service object, the returned type depends on the service type:
// For http endpoint:
$client = $reference->getAs(JDBCClient::class);
// Do something with the client...
// When done with the service
$reference->release();
前面提到,服务发现使用了服务类型的概念,来封装各种服务的差异性。
目前服务发现组件提供了几种默认的服务类型:
HttpEndpoint
- 为REST API服务提供的类型,服务对象的类型是一个配置好了host和port的 HttpClient
(其location表现为一个url)
EventBusService
- 服务代理,服务对象是一个代理,它的类型是所代理的接口(其location表现为一个Event Bus的address地址)
MessageSource
- 消息源服务,服务对象的类型是一个 MessageConsumer
(其location表现为一个Event Bus的address地址)
JDBCDataSource
- JDBC数据源服务,服务对象的类型是一个 JDBCClient
(该Client的配置参数,将从location、元数据和服务消费者传递的参数中获取)
RedisDataSource
- Redis数据源服务,服务对象的类型是一个 RedisClient
(该client的配置参数,将从location、元数据和服务消费者传递的参数中获取)
MongoDataSource
- Mongo数据源服务,服务对象的类型一个 MongoClient
(该client的配置参数,将从location、元数据和服务消费者传递的参数中获取)
本节将详细介绍一下服务类型,以及如何使用服务发现框架已提供的几种服务类型。
某些服务记录也可以不带有类型( ServiceType::UNKNOWN
)。通过这种服务记录,是无法获取到服务引用的,但是你可以通过服务记录( Record
)的`location`和`metadata`来创建连接的细节。
使用这种服务,将不会产生服务使用的事件。
一个 HTTP 端点(endpoint),就是一个REST API或可以通过HTTP请求访问的服务。HTTP Endpoint服务对象就是一个配置了host、port和ssl的 `HttpClient`对象。
要发布一个HTTP Endpoint服务,你需要一个 Record
对象。你可以通过调用
HttpEndpoint::createRecord
创建这样一个服务记录对象。
下面的代码片段,展示了如何通过 HttpEndpoint
接口创建一个 Record
:
<?php
use io\vertx\jphp\servicediscovery\types\HttpEndpoint;
$record1 = HttpEndpoint::createRecord("some-http-service", "localhost", 8433, "/api");
$discovery->publish($record1, function ($ar, $ar_err) {
// ...
});
$record2 = HttpEndpoint::createRecord("some-other-name", true, "localhost", 8433, "/api", array(
"some-metadata" => "some value"
));
当你在容器或云上部署你的服务时,可能你不能确定公开的IP地址和端口。所以,服务的发布必须通过其他拥有这些信息的实体来进行,这通常是一个桥接对象(bridge)。
一旦一个HTTP Endpoint服务发布好了,服务消费者就可以获取到这个服务。对应的服务对象是一个
HttpClient
实例,并且已经配置好了host和port参数。
<?php
use io\vertx\jphp\core\http\HttpClient;
// Get the record
$discovery->getRecord(array(
"name" => "some-http-service"
), function ($ar, $ar_err) {
if ($ar != null && $ar != null) {
// Retrieve the service reference
$reference = $discovery->getReference($ar);
// Retrieve the service object
$client = $reference->getAs(HttpClient::class);
// You need to path the complete path
$client->getNow("/api/persons", function ($response) {
// ...
// Dont' forget to release the service
$reference->release();
});
};
});
你也可以使用
HttpEndpoint::getClient
这个方法,一步就完成服务查找和服务获取:
<?php
use io\vertx\jphp\servicediscovery\types\HttpEndpoint;
use io\vertx\jphp\servicediscovery\ServiceDiscovery;
HttpEndpoint::getClient($discovery, array(
"name" => "some-http-service"
), function ($ar, $ar_err) {
if ($ar != null) {
$client = $ar;
// You need to path the complete path
$client->getNow("/api/persons", function ($response) {
// ...
// Dont' forget to release the service
ServiceDiscovery::releaseServiceObject($discovery, $client);
});
};
});
在第二种写法中,服务对象的释放是通过
ServiceDiscovery::releaseServiceObject
这个方法完成的,因此在这种情况下你是不需要持有一个服务引用的。
<?php
use io\vertx\jphp\ext\web\client\WebClient;
// Get the record
$discovery->getRecord(array(
"name" => "some-http-service"
), function ($ar, $ar_err) {
if ($ar != null && $ar != null) {
// Retrieve the service reference
$reference = $discovery->getReference($ar);
// Retrieve the service object
$client = $reference->getAs(WebClient::class);
// You need to path the complete path
$client->get("/api/persons")->send(function ($response, $response_err) {
// ...
// Dont' forget to release the service
$reference->release();
});
};
});
另外一种写法,通过对应的服务类型接口获取的方式:
<?php
use io\vertx\jphp\servicediscovery\types\HttpEndpoint;
use io\vertx\jphp\servicediscovery\ServiceDiscovery;
HttpEndpoint::getWebClient($discovery, array(
"name" => "some-http-service"
), function ($ar, $ar_err) {
if ($ar != null) {
$client = $ar;
// You need to path the complete path
$client->get("/api/persons")->send(function ($response, $response_err) {
// ...
// Dont' forget to release the service
ServiceDiscovery::releaseServiceObject($discovery, $client);
});
};
});
Event Bus 服务是一种服务代理,是基于Event Bus实现的一种异步RPC服务。当从一个Event Bus服务中获取一个服务对象时,你实际上得到的某个服务类的服务代理。你也可以使用 EventBusService
接口的辅助方法来获得服务代理。
注意服务代理(服务实现和服务接口)都需要用Java语言开发。
要发布一个Event Bus服务,你需要创建一个 Record
对象:
<?php
use io\vertx\jphp\servicediscovery\types\EventBusService;
$record = EventBusService::createRecord("some-eventbus-service", "address", "examples.MyService", array(
"some-metadata" => "some value"
));
$discovery->publish($record, function ($ar, $ar_err) {
// ...
});
要调用(消费)Event Bus服务,你可以通过先获取到服务记录然后获取服务引用的方式,也可以直接通过
EventBusService
接口,将两步合并成一次方法调用。
消息源服务,就是通过Event Bus发送消息到某个地址的组件。消息源服务的Client是
MessageConsumer
。
消息源服务的 location
是消息所发送的Event Bus 地址。
和其他服务类型一样,发布一个消息源服务包含两个步骤:
通过 MessageSource
接口创建一条服务记录
发布这条服务记录
<?php
use io\vertx\jphp\servicediscovery\types\MessageSource;
$record = MessageSource::createRecord("some-message-source-service", "some-address");
$discovery->publish($record, function ($ar, $ar_err) {
// ...
});
$record = MessageSource::createRecord("some-other-message-source-service", "some-address", "examples.MyData");
在第二个 Record
创建时,我们同时指明了消息体(payload)的类型,这不是必须的。
在服务消费端,你可以手动获取服务记录和服务引用,也可以使用
MessageSource
接口提供的辅助方法直接获取。
第一种方式对应的代码示例如下:
<?php
use io\vertx\jphp\core\eventbus\MessageConsumer;
// Get the record
$discovery->getRecord(array(
"name" => "some-message-source-service"
), function ($ar, $ar_err) {
if ($ar != null && $ar != null) {
// Retrieve the service reference
$reference = $discovery->getReference($ar);
// Retrieve the service object
$consumer = $reference->getAs(MessageConsumer::class);
// Attach a message handler on it
$consumer->handler(function ($message) {
// message handler
$payload = $message->body();
});
};
});
如果使用 MessageSource
接口,代码如下:
<?php
use io\vertx\jphp\servicediscovery\types\MessageSource;
MessageSource::getConsumer($discovery, array(
"name" => "some-message-source-service"
), function ($ar, $ar_err) {
if ($ar != null) {
$consumer = $ar;
// Attach a message handler on it
$consumer->handler(function ($message) {
// message handler
$payload = $message->body();
});
// ...
};
});
数据源指的是数据库或数据存储。JDBC数据源通过JDBC驱动访问数据库,JDBC数据源服务对象是是 JDBCClient
实例。
和其他服务类型一样,发布 JDBC 数据源服务共两个步骤:
通过 JDBCDataSource
接口创建服务记录
发布服务记录
<?php
use io\vertx\jphp\servicediscovery\types\JDBCDataSource;
$record = JDBCDataSource::createRecord("some-data-source-service", array(
"url" => "some jdbc url"
), array(
"some-metadata" => "some-value"
));
$discovery->publish($record, function ($ar, $ar_err) {
// ...
});
JDBC 数据源可以代表各种类型的数据库,而这些数据库的访问方式一般是不同的,服务记录很难有统一结构。在服务记录中,location
由一个简单的JSON对象组成,里面包含访问数据源的各种属性(JDBC URL、用户名、密码等)。这些属性既依赖于数据库,同时也依赖于所使用的连接池。
如前所述,访问数据源的方式依赖于数据源本身。要创建一个
JDBCClient
,你需要同时提供:服务记录位置信息、元数据以及服务消费者提供的JSON对象:
<?php
use io\vertx\jphp\ext\jdbc\JDBCClient;
// Get the record
$discovery->getRecord(array(
"name" => "some-data-source-service"
), function ($ar, $ar_err) {
if ($ar != null && $ar != null) {
// Retrieve the service reference
$reference = $discovery->getReferenceWithConfiguration($ar, array(
"username" => "clement",
"password" => "*****"
));
// Retrieve the service object
$client = $reference->getAs(JDBCClient::class);
// ...
// when done
$reference->release();
};
});
你也可以使用 JDBCDataSource
接口的辅助方法,来查询和获取服务对象:
<?php
use io\vertx\jphp\servicediscovery\types\JDBCDataSource;
use io\vertx\jphp\servicediscovery\ServiceDiscovery;
JDBCDataSource::getJDBCClient($discovery, array(
"name" => "some-data-source-service"
), array(
"username" => "clement",
"password" => "*****"
), function ($ar, $ar_err) {
if ($ar != null) {
$client = $ar;
// ...
// Dont' forget to release the service
ServiceDiscovery::releaseServiceObject($discovery, $client);
};
});
Redis 数据源服务是专门为Redis提供的服务类型,对应服务对象是 RedisClient
。
发布一个 Redis 数据源服务共两个步骤:
通过 RedisDataSource
接口创建一条服务记录
发布这个服务记录
<?php
use io\vertx\jphp\servicediscovery\types\RedisDataSource;
$record = RedisDataSource::createRecord("some-redis-data-source-service", array(
"url" => "localhost"
), array(
"some-metadata" => "some-value"
));
$discovery->publish($record, function ($ar, $ar_err) {
// ...
});
这里的 location
是一个JSON对象,包含访问Redis数据源的属性(URL、端口等)。
如前所述,访问数据源的方式依赖于数据源本身。要创建一个
RedisClient
,你需要同时提供:服务记录位置信息、元数据以及服务消费者提供的JSON对象:
<?php
use io\vertx\jphp\redis\RedisClient;
// Get the record
$discovery->getRecord(array(
"name" => "some-redis-data-source-service"
), function ($ar, $ar_err) {
if ($ar != null && $ar != null) {
// Retrieve the service reference
$reference = $discovery->getReference($ar);
// Retrieve the service instance
$client = $reference->getAs(RedisClient::class);
// ...
// when done
$reference->release();
};
});
你也可以利用 RedisDataSource
接口的辅助方法来查询和获取服务对象:
<?php
use io\vertx\jphp\servicediscovery\types\RedisDataSource;
use io\vertx\jphp\servicediscovery\ServiceDiscovery;
RedisDataSource::getRedisClient($discovery, array(
"name" => "some-redis-data-source-service"
), function ($ar, $ar_err) {
if ($ar != null) {
$client = $ar;
// ...
// Dont' forget to release the service
ServiceDiscovery::releaseServiceObject($discovery, $client);
};
});
Mongo 数据源服务是专门为 MongoDB 提供的一种服务类型,对应的服务对象是 MongoClient
。
发布一个 Mongo 数据源服务需要两步:
通过 MongoDataSource
接口创建一条服务记录
发布这条服务记录
<?php
use io\vertx\jphp\servicediscovery\types\MongoDataSource;
$record = MongoDataSource::createRecord("some-data-source-service", array(
"connection_string" => "some mongo connection"
), array(
"some-metadata" => "some-value"
));
$discovery->publish($record, function ($ar, $ar_err) {
// ...
});
其中`location`是一个JSON对象,包含了访问Mongo数据源的所有属性(URL、端口等)
如前所述,访问数据源的方式依赖于数据源本身。要创建一个
MongoClient
,你需要同时提供:服务记录位置信息、元数据以及服务消费者提供的JSON对象:
<?php
// Get the record
$discovery->getRecord(array(
"name" => "some-data-source-service"
), function ($ar, $ar_err) {
if ($ar != null && $ar != null) {
// Retrieve the service reference
$reference = $discovery->getReferenceWithConfiguration($ar, array(
"username" => "clement",
"password" => "*****"
));
// Retrieve the service object
$client = $reference->get();
// ...
// when done
$reference->release();
};
});
你也可以利用 MongoDataSource
接口中的辅助方法来完成服务对象的查找和获取:
<?php
use io\vertx\jphp\servicediscovery\types\MongoDataSource;
use io\vertx\jphp\servicediscovery\ServiceDiscovery;
MongoDataSource::getMongoClient($discovery, array(
"name" => "some-data-source-service"
), array(
"username" => "clement",
"password" => "*****"
), function ($ar, $ar_err) {
if ($ar != null) {
$client = $ar;
// ...
// Dont' forget to release the service
ServiceDiscovery::releaseServiceObject($discovery, $client);
};
});
每当服务发布或者取消发布,都会有相应的事件发送到 vertx.discovery.announce
这个地址。这个地址可以通过 ServiceDiscoveryOptions
配置。
收到的`Record`中有个`status`字段,用来表示服务的状态:
UP
: 服务已经可以使用了
DOWN
: 服务不再可用
OUT_OF_SERVICE
: 服务目前不可用,但是过段时间会继续提供服务。
每当有一个服务引用被绑定或者被释放,都会有相应的事件发送到 vertx.discovery.usage
这个地址。这个地址可以通过 ServiceDiscoveryOptions
配置。
通过这个事件,可以监听服务的使用和服务的映射。
收到的消息是一个包含如下内容的 JsonObject
对象:
在 record
属性中,包含了服务记录信息
在 type
属性中记录了事件的类型,类型分为`bind`和`release`
在 id
属性中记录了服务发现实例的ID(服务发现实例的名称或节点ID)
其中 id
可以通过 ServiceDiscoveryOptions
进行配置。默认情况下,在单节点时它的值是`localhost`,在集群模式时是节点的ID。
你也可以通过
setUsageAddress
方法,将事件发送地址设置为`null`,这样就可以禁用服务使用情况的监听功能了。
通过桥接器(bridge),你可以从其他服务发现组件中导入和导出服务,比如Docker,Kubernetes,Consul等。每种类型的桥接器,决定了服务如何导入和导出,并且不一定都是双向的。
要想自定义桥接器,你可以通过实现 ServiceImporter
接口,然后再使用
registerServiceImporter
方法注册一下。
你可以通过第二个参数传递一些可选的配置信息给桥接器。
当桥接器注册后,
#start)
方法将会被调用,这样你可以对桥接器进行一些配置。当桥接器配置好了,已经准备导入导出初始的服务时,必须 complete
所传递的 Future
。如果桥接器的启动方法是阻塞型的,那么就必须使用
executeBlocking
方法进行封装,并且`complete`所传递的 Future
对象。
当服务发现实例被关闭的时候,对应的桥接器也一块被关闭了。执行关闭操作的时候,服务发现组件会调用
close
方法以进行资源的释放以及移除导入/导出的服务。这个方法必须调用所传递的
Future
的`complete`方法,来通知调用者关闭操作已经完成。
需要提醒的是,在一个集群中,只需要有一个节点注册了服务桥接器,集群中所有成员就都能使用了。
Vert.x 服务发现组件除了支持桥接器机制以外,还提供了一些现成的桥接器。
Unresolved directive in index.adoc - include::consul-bridge.adoc[]
Unresolved directive in index.adoc - include::kubernetes-bridge.adoc[]
Unresolved directive in index.adoc - include::zookeeper-bridge.adoc[]
Unresolved directive in index.adoc - include::docker-links-bridge.adoc[]
Vert.x服务发现框架还提供了一些现成的后端存储机制支持。
Unresolved directive in index.adoc - include::redis-backend.adoc[]