Elasticsearch:为 Elasticsearch 8.x 引入新的 PHP 客户端
2022年6月12日 | by mebius
Elasticsearch 8 的新 PHP 客户端已从头开始重写。 除了采用 PSR 标准外,我们还重新设计了架构并将 HTTP 传输层移到了外部。 由于 HTTPlug 库,现在还可以使用可插拔系统。
继续阅读以探索:
- PHP 客户端的新架构和特性
- 如何使用 HTTP 消息的 PSR-7标准使用端点和管理错误
- 如何使用异步方法与 Elasticsearch 交互
旧客户端
elasticsearch-php 库是使用 PHP 编程 Elasticsearch 的官方客户端。这个库使用一个 main 客户端类公开了 Elasticsearch 的所有 400 多个端点。在这个库的第 7 版中,所有端点都使用函数公开——例如,indexAPI 映射到方法 Client::index()。
这些函数返回一个关联数组,该数组是来自 Elasticsearch 的 HTTP 响应的反序列化。通常,此响应由 JSON 消息表示。使用 PHP 的 json_decode() 函数将此消息转换为数组。
如果出现错误,客户端会根据问题抛出异常。例如,如果 HTTP 响应是 404,客户端会抛出 Missing404Exception。如果要检索 HTTP 响应本身,则需要使用以下代码从客户端获取最后一个响应:
$response = $client->info();
$last = $client->transport->getLastConnection()->getLastRequestInfo();
$request = $last['request']; // associative array of the HTTP request
var_dump($request);
$response = $last['response']; // associative array of the HTTP response
echo $response['status']; // 200
echo $response['body']; // the body as string
HTTP 请求和响应通过以下几种方法从 transport 层(客户端的属性)检索:getLastConnection() 和 getLastRequestInfo()。
这段代码不能提供良好的开发体验,因为关联数组 $response 的键非常多,来自 PHP 的 cURL 扩展的使用。
新的客户
我们从头开始构建新的 elasticsearch-php 8 有很多原因:开发人员体验、新的 PHP 标准、更开放的架构和性能。
安装了大约 7000 万次,我们不希望版本 8 有很多 BC 中断。我们使用向后兼容的方法提供与版本 7 相同的 API。这意味着你通常可以使用相同的代码连接到 Elasticsearch 并执行端点调用 。不同之处在于响应。 在版本 8 中,响应是 Elasticsearch 响应的对象,实现了 PSR-7 响应接口和 PHP 的 ArrayAccess 接口。
等一下 — 这不是 BC 的中断吗? 幸运的是,我们实现了 ArrayAccess 接口,你可以继续以数组的形式消费响应,如下所示:
发现差异:命名空间已更改! 我们引入了 Elastic 根命名空间。 其他代码看起来相同,但引擎盖下有很大的变化。
正如我们所提到的,版本 8 中的 $response 是一个对象,而版本 7 中这是一个关联数组。 如果你想要与版本 7 完全相同的行为,你可以使用函数 $response->asArray() 将响应序列化为数组。
我们还提供 asObject()、asString() 和 asBool() 函数将主体序列化为 PHP 标准类 (stdClass) 的对象,作为字符串或布尔值(如果 2xx 响应则为 true,否则为 false)。
例如,你可以使用之前的 info() 端点,如下所示:
$client = ClientBuilder::create()
->setHosts(['localhost:9200'])
->build();
$response = $client->info();
echo $response['version']['number'];
echo $response->version->number; // 8.0.0
var_dump($response->asObject()); // response body content as stdClass object
var_dump($response->asString()); // response body as string (JSON)
var_dump($response->asBool()); // true if HTTP response code 2xx
$response 能够作为实现 PHP 的 _get() 魔术方法的对象访问响应主体。
如果要读取 HTTP 响应,则不需要从 Client 对象中恢复最后一条消息; 你可以在 $response 本身中访问 PSR-7 消息,如下所示:
echo $response->getStatusCode(); // 200, since $response is PSR-7
echo (string) $response->getBody(); // Response body in JSON
这是一个很大的优势,尤其是当你使用异步时。 事实上,如果你使用异步编程,则无法从客户端检索到最后的响应。 不能保证最后一个响应就是你要查找的响应(本文后面将详细介绍异步操作)
自动完成的端点参数
我们使用 Psalm 项目的类对象数组在 elasticsearch-php 版本 8 中添加了自动完成功能。 Psalm 是一个静态分析工具,允许开发人员使用特殊的 phpDoc 属性来修饰代码。 其中一个属性是@psalm-type,它可以指定关联数组的键类型。 我们使用标准的 phpDoc @param 应用了 Psalm 类型。 每个 PHP 客户端端点都有一个输入参数,即 $params 数组。 例如,这里报告的是 index() 端点部分:
/**
* Creates or updates a document in an index.
*
* @see https://www.elastic.co/guide/en/elasticsearch/reference/master/docs-index_.html
*
* @param array{
* id: string, // Document ID
* index: string, // (REQUIRED) The name of the index
* wait_for_active_shards: string, // Sets the number of shard copies …
* op_type: enum, // Explicit operation type. Defaults to `index` for requests…
* refresh: enum, // If `true` then refresh the affected shards to make this operation…
* routing: string, // Specific routing value
* timeout: time, // Explicit operation timeout
* version: number, // Explicit version number for concurrency control
* version_type: enum, // Specific version type
* if_stgcodeeq_no: number, // only perform the index operation if the last operation…
* if_primary_term: number, // only perform the index operation if the last operation…
* pipeline: string, // The pipeline id to preprocess incoming documents with
* require_alias: boolean, // When true, requires destination to be an alias…
* pretty: boolean, // Pretty format the returned JSON response. (DEFAULT: false)
* human: boolean, // Return human readable values for statistics. (DEFAULT: true)
* error_trace: boolean, // Include the stack trace of returned errors. (DEFAULT: false)
* source: string, // The URL-encoded request definition. Useful for libraries…
* filter_path: list, // A comma-separated list of filters used to reduce the response.
* body: array, // (REQUIRED) The document
* } $params
*/
public function index(array $params = [])
所有参数都用名称(索引)指定,包括类型(字符串)和描述参数的注释(索引的名称)。 所需参数使用 REQUIRED 注释指定。
你可以使用以前的表示法在 IDE 中进行自动完成。 例如,使用 PhpStorm,你可以安装 deep-assoc-completion 免费插件以启用具有 @psalm-type 属性的 PHP 关联数组自动完成。
Elasticsearch PHP client 8.x deep-assoc-completion
Elasticsearch PHP client 8.x deep-assoc-completion_哔哩哔哩_bilibili
即使此版本仍在开发中,Visual Studio Code也可以使用 deep-assoc-completion。
可插拔架构
我们在版本 8 中所做的另一个更改是从库中拆分 HTTP 传输层。 我们创建了 elastic-transport-php 库,它是一个 PSR-18 客户端,用于连接到 PHP 中的 Elastic 产品。 这个库不仅被 elasticsearch-php 使用,还被其他项目(如 enterprise-search-php)使用。
该库基于可插拔架构,这意味着您可以将其配置为使用以下接口的特定实现:
- PSR-18 客户端,使用 ClientInterface
- 一个节点池,使用 NodePoolInterface
- PSR-3 记录器,使用 LoggerInterface
elasticsearch-php 版本 8 使用 elastic-transport-php 作为依赖项。 这意味着你可以使用自定义 HTTP 库、自定义节点池或自定义记录器连接到 Elasticsearch。
我们使用 HTTPlug 库来执行 PHP 应用程序中可用的 PSR-18 和 PSR-7 库的自动发现。 默认情况下,如果应用程序没有安装 HTTP 库,我们使用 Guzzle。
例如,你可以使用Symfony HTTP 客户端,如下所示:
use SymfonyComponentHttpClientPsr18Client;
$client = ClientBuilder::create()
->setHttpClient(new Psr18Client)
->build();
或者,你可以使用 Monolog 记录器库,如下所示:
use MonologLogger;
use MonologHandlerStreamHandler;
$logger = new Logger('name');
$logger->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
$client = ClientBuilder::create()
->setLogger($logger)
->build();
有关如何自定义客户端的更多信息,请查看配置页面。
连接到 Elastic Cloud
Elastic Cloud 是 Elastic 提供的 PaaS 解决方案。 要连接到 Elastic Cloud,你只需要 Cloud ID 和 API 密钥。
可以在 Elastic Cloud 仪表板的我的部署页面中检索 Cloud ID。 API 密钥可以从安全页面设置中的管理部分生成。
你可以阅读 PHP 客户端文档的连接部分以获取更多信息。
收集 Cloud ID 和 API 密钥后,你可以使用 elasticsearch-php 连接到你的 Elastic Cloud 实例,如下所示:
$client = ClientBuilder::create()
->setElasticCloudId('insert here the Cloud ID')
->setApiKey('insert here the API key')
->build();
默认安全
如果你在基础架构中安装了 Elasticsearch 8,则可以使用启用了 TLS(传输层安全)的 PHP 客户端。 Elasticsearch 8 默认提供安全性,这意味着它使用 TLS 来保护客户端和服务器之间的通信。
为了配置 elasticsearch-php 以连接到 Elasticsearch 8,你需要拥有证书授权文件 (CA)。
你可以通过不同的方式安装 Elasticsearch。 如果使用 Docker,则需要执行以下命令:
docker pull docker.elastic.co/elasticsearch/elasticsearch:8.1.0
安装 Docker 映像后,你可以使用单节点集群配置执行 Elasticsearch,如下所示:
docker network create elastic
docker run --name es01 --net elastic -p 9200:9200 -p 9300:9300 -it docker.elastic.co/elasticsearch/elasticsearch:8.1.0
此命令创建一个弹性 Docker 网络并使用端口 9200(默认)启动 Elasticsearch。
当你运行 Docker 映像时,将为 Elastic 用户生成一个密码并将其打印到终端(你可能需要在终端中向后滚动一点才能查看它)。 你必须复制它才能连接到 Elasticsearch。
现在 Elasticsearch 正在运行,我们可以获得 http_ca.crt 文件证书。 使用以下命令从 Docker 实例复制它:
docker cp es01:/usr/share/elasticsearch/config/certs/http_ca.crt .
一旦我们有了在 Elasticsearch 启动期间复制的 http_ca.crt 证书和密码,我们就可以使用它进行如下连接:
$client = ClientBuilder::create()
->setHosts(['https://localhost:9200'])
->setBasicAuthentication('elastic', 'password copied during ES start')
->setCABundle('path/to/http_ca.crt')
->build();
在异步模式下使用客户端
PHP 客户端提供了为每个端点执行异步调用的可能性。 在版本 7 中,你需要在作为端点参数传递的客户端密钥中指定一tgcode个特殊的 future => lazy,如下所示:
$params = [
'index' => 'my-index',
'client' => [
'future' => 'lazy'
],
'body' => [
'foo' => 'bar'
]
];
$response = $client->index($params);
前面的示例使用异步 HTTP 调用在 Elasticsearch 中索引 { “foo”: “bar” } 文档。 $response 是一个 future,而不是实际的响应。
future 代表 future 的计算,并且充当占位符。 你可以像常规对象一样在代码周围传递 future。 当你需要结果值时,你可以解决 future。 如果 future 已经解决(由于某些其他活动),则这些值立即可用。 如果 future 还没有被解析,解析会阻塞,直到这些值变得可用(例如,在 API 调用完成之后)。
在版本 7 中,future 实际上是 RingPHP 项目的 Promise。 在版本 8 中,如果你想使用异步,你需要为您的 HTTP 客户端安装特定的适配器。 例如,如果你使用 Guzzle 7(elasticsearch-php 的默认 HTTP 库),则需要安装 php-http/guzzle7-adapter,如下所示:
composer require php-http/guzzle7-adapter
要使用异步调用执行端点,你需要使用 Client::setAsync(true) 函数启用它,如下所示:
$client->setAsync(true);
$params = [
'index' => 'my-index',
'body' => [
'foo' => 'bar'
]
];
$response = $client->index($params);
如果要为下一个端点禁用异步,则需要再次将 setAsync 设置为 false。
异步调用的响应是 HTTPlug 库的 Promise 对象。 这个 Promise 遵循 Promises/A+ 标准。 Promise 代表异步操作的最终结果。
要获得响应,你需要等待该响应到达。 这将阻塞等待响应的执行,如下所示:
$response = $client->index($params);
$response = $response->wait();
printf("Body response:n%sn", $response->asString());
与 Promise 交互的主要方式是通过其 then 方法,该方法注册回调以接收 Promise 的最终值或无法实现 Promise 的原因。
$response = $client->index($params);
$response->then(
// The success callback
function (ResponseInterface $response) {
// $response is ElasticElasticsearchResponseElasticsearch
printf("Body response:n%sn", $response->asString());
},
// The failure callback
function (Exception $exception) {
echo 'Houston, we have a problem';
throw $exception;
}
);
$response = $response->wait();
需要最后一个 $response->wait() 来解决上面示例中的执行调用。
更少的代码和内存使用
新的 Elasticsearch PHP 客户端与版本 7 相比使用的代码更少。特别是,elasticsearch-php 版本 8 由 6,522 行代码 + elastic-transport-php 的 1,025 行代码组成,总共 7,547 行。 在版本 7 中,我们有 20,715 行代码,因此新版本 8 的大小约为之前的三分之一。
在内存使用方面,elasticsearch-php 版本 8 实现了延迟加载机制来优化 API 命名空间加载。 这意味着如果您只使用所有 400 多个端点的一个子集,你将不会在内存中加载所有规范。
总结
Elasticsearch 8 带来了一些令人兴奋的改进。 从新架构和保持与版本 7 的向后兼容性的能力,到默认的安全设置和异步模式的优势,可能性从未像现在这样无穷无尽。
最好的入门方法是使用 Elastic Cloud。 立即开始免费试用 Elastic Cloud!
原tgcode文:Introducing the new PHP client for Elasticsearch 8 | Elastic Blog
文章来源于互联网:Elasticsearch:为 Elasticsearch 8.x 引入新的 PHP 客户端
相关推荐: Elasticsearch:为 Elasticsearch 8.x 引入新的 PHP 客户端
作者:Enrico Zimuel Elasticsearch 8 的新 PHP 客户端已从头开始重写。 除了采用 PSR 标准外,我们还重新设计了架构并将 HTTP 传输层移到了外部。 由于 HTTPlug 库,现在还可以使用可插拔系统。 继续阅读以探索: P…