blog-post

Manticore Buddy: 可插拔设计

本文适用于 Buddy v1.x。 在此查看最新版本更新

大家好。关于 Manticore Buddy 的激动人心的消息:我们完成了迁移到可插拔设计!这意味着您可以将自己的 Manticore Search SQL/JSON 查询构建为插件,在 packagist.org 上发布,并使用 CREATE PLUGIN SQL 命令安装它。让我们深入了解可插拔系统的架构以及一个简单的教程,帮助您入门。

架构

介绍

如果您不熟悉 Buddy,建议您阅读以下博客文章:

这些是我们之前发布的文章。

最初,Buddy 是用 PHP 开发的,所有扩展和额外的命令处理程序都在同一代码库中。在 Manticore Search 6 发布后,我们收到了有关添加自定义功能的询问,并意识到当前系统缺乏灵活性。这就是我们决定迁移到可插拔架构的原因。

我们认为,软件开发中最重要的原则是不要重新发明轮子,并保持事物简单。因此,我们没有花太多时间考虑,而是选择了 PHP 领域中最知名和流行的软件包管理器来管理插件系统 – Packagist

插件类型

有三种类型的插件:

  • core - 默认包含在 Buddy 中,是您安装 Manticore 时收到的包的一部分。
  • local - 用于开发和调试,然后再发布。
  • external - 使用 CREATE PLUGIN 命令安装。

本教程说明如何首先创建一个 local 插件,然后将其转换为 external 插件,使任何 Manticore 用户都可以通过使用您的插件来增强他们的设置。

请求 -> 响应流

为了使其工作,我们将 Buddy 和所有插件中常用的组件提取到一个新包中 – Buddy Core 。在开发插件时,它应该被包含在内,以便在您的 IDE 中使事情更简单,并支持提示和自动完成功能。

使 Buddy 作为守护进程运行并执行内部任务的代码,包括连接 Core 和插件源代码,称为 Buddy Base。这是一个 ReactPHP 应用程序,使用 Buddy Core ,并具有基本逻辑,使所有内容正常工作并找到命令中要启动的正确插件。

为了更清晰地理解这个过程,以下是一个图示,说明请求 → 响应流程:

Manticore Buddy 请求到响应流程

教程

为了让您更轻松地入门并开展自己的插件开发,让我们来看看如何为 Buddy 构建一个支持 SHOW HOSTNAME 命令并返回当前主机机器的主机名的简单插件。这将帮助您理解在创建自己的插件时应遵循的过程,同时也是一个极其简单的示例,适合入门。如果我们在实现其逻辑之前检查 SHOW HOSTNAME 在 Manticore Search 中的功能,我们可以期望得到如下结果:

mysql> SHOW HOSTNAME;
ERROR 1064 (42000): sphinxql: 语法错误,意外标识符,期望在 'HOSTNAME' 附近的 VARIABLES

准备工作

我们首先要做的就是准备我们的开发环境。您只需执行一次此操作,之后可以从 GIT 代码仓库 更新 Buddy,以接收最新更新。让我们打开终端,并将 Buddy 克隆到我们机器上的一个名为 manticoresearch-buddy 的文件夹中,并导航到克隆的文件夹。

git clone https://github.com/manticoresoftware/manticoresearch-buddy.git
cd manticoresearch-buddy
git checkout v1.x

现在您已经拥有了 Buddy 的完整源代码,但没有安装任何软件包。Buddy 使用 Composer 管理其依赖关系,我们需要首先安装它们以运行应用程序。但是,在那之前,我们将运行一个特殊的 Docker 容器,该容器是我们专为开发准备的。Manticore Executor Kit 镜像包含了平稳进行 Manticore Buddy 开发所需的所有必要工具,无论您是创建新插件还是为 Buddy 本身做贡献。

docker pull ghcr.io/manticoresoftware/manticoresearch:test-kit-6.2.12
docker create --privileged --entrypoint bash \
  -v $(pwd):/workdir -w /workdir --name manticore-buddy \
  -it ghcr.io/manticoresoftware/manticoresearch:test-kit-6.2.12
docker start manticore-buddy

恭喜!您现在已启动一个包含 Manticore SearchManticore Executor 的 Docker 容器,以便开发 Buddy 和插件。工作目录设置为 /workdir,您的源文件夹绑定到容器。您接下来的步骤是安装 Composer 依赖项。请记住,此命令和所有后续命令必须在 manticore-buddy 容器内部执行。

docker exec -it manticore-buddy bash
composer install

您的 Buddy 现在准备好进行使用、开发、调试和测试。接下来,您需要完成两件事:

  1. 首先,编辑 /etc/manticoresearch/manticore.conf 并添加 buddy_path 以及我们的源代码。这将让 Manticore Search 知道我们需要使用来自源代码的运行 Buddy 的自定义路径,而不是来自已安装模块的路径。将以下内容添加到 searchd 部分:
    buddy_path = manticore-executor /workdir/src/main.php --debug
    
  2. 其次,尝试运行 searchd(Manticore Search 服务器),确保它正常工作。
    # 启动容器内的守护进程并保持在前台
    searchd --nodetach
    
    你应该看到几行以 [BUDDY] 开头。当你想要停止该过程时,只需使用 Ctrl + C

此时,所有内容已准备就绪,可以为 Buddy 准备开发环境并开始实现新插件。你仍然可以对其进行实验,并尝试执行一些查询以确保其正常工作。例如,打开另一个终端窗口并运行以下命令:

$ docker exec -it manticore-buddy mysql -h0 -P9306
欢迎使用 MariaDB 监控程序。命令以 ; 或 \g 结束。
你的 MySQL 连接 ID 是 6
服务器版本:6.0.5 3bcbd00fa@230320 dev (columnar 2.0.5 8171c1a@230320) (secondary 2.0.5 8171c1a@230320) git branch HEAD (no branch)

版权所有 (c) 2000, 2018, Oracle, MariaDB Corporation Ab 和其他公司。

输入 'help;''\h' 获取帮助。输入 '\c' 清除当前输入语句。

MySQL [(none)]> show queries;
+------+--------------+----------+-----------------+
| id   | query        | protocol | host            |
+------+--------------+----------+-----------------+
|   10 | select       | http     | 127.0.0.1:19148 |
|    9 | show queries | mysql    | 127.0.0.1:54484 |
+------+--------------+----------+-----------------+
2 rows in set (0.008 sec)

MySQL [(none)]>

你刚刚将命令 SHOW QUERIES 发送给 Manticore Search,然后被路由到 Buddy 并成功执行。你可以在第一个终端标签的 searchd 日志中看到 Buddy 收到的内容及其响应。因此,我们可以确认 Buddy 功能正常,一切设置正确。

插件模板

我们创建了一个 特殊的 GitHub 模板库 ,强烈建议使用此库创建任何 Manticore Buddy 插件。所有插件应以 buddy-plugin-[your-name] 为前缀,其中 [your-name] 是你插件的名称。例如,由于我们正在开发一个主机名选择插件,我们将其命名为 buddy-plugin-show-hostname。所以:

  1. 打开 https://github.com/manticoresoftware/buddy-plugin-template
  2. 选择 v1.x 分支
  3. 点击 Use this template 然后 Create a new repository
  4. 填写表单,输入你的库名称并创建它。确保你的库名称以 buddy-plugin- 开头,因为这是强制性的。
  5. git clone 你的新仓库到你之前部署的 Manticore Buddy 仓库的 plugins 目录。请注意,最好在容器外执行此操作,尤其是当你从私有仓库克隆时。

https://github.com/manticoresoftware/buddy-plugin-show-hostname (分支:v1.x),你可以找到整个任务的实现。如果你不想深入了解细节,你可以简单地 git clone 而不是你的派生库。不过,如果你想掌握创建插件的过程,让我们继续。

添加真实代码

现在我们需要按照以下步骤更新我们的模板,以与我们的新插件相关的数据。

  1. 首先,我们打开 composer.json 并更新插件名称、描述和命名空间。因此,最终你的更改将如下所示(确保使用你的插件库名称):
diff --git a/composer.json b/composer.json
index 23c252b..f36cb6e 100644
--- a/composer.json
+++ b/composer.json
@@ -1,11 +1,11 @@
 {
- "name": "manticoresoftware/buddy-plugin-template",
- "description": "The Buddy template handler plugin",
+ "name": "manticoresoftware/buddy-plugin-show-hostname",
+ "description": "The Buddy SHOW hostname handler plugin",
  "type": "library",
  "license": "GPL-2.0-or-later",
  "autoload": {
    "psr-4": {
-     "Manticoresearch\\Buddy\\Plugin\\Template\\": "src/"
+     "Manticoresearch\\Buddy\\Plugin\\ShowHostname\\": "src/"
    }
  },
  "authors": [
  1. 我们还需要更新两个类的命名空间 – PayloadHandler。差异应如下所示:
diff --git a/src/Handler.php b/src/Handler.php
index 3756dc3..07fcfd0 100644
--- a/src/Handler.php
+++ b/src/Handler.php
@@ -8,7 +8,7 @@
   version. You should have received a copy of the GPL license along with this
   program; if you did not, you can find it at http://www.gnu.org/
 */
-namespace Manticoresearch\Buddy\Plugin\Template;
+namespace Manticoresearch\Buddy\Plugin\ShowHostname;

 use Manticoresearch\Buddy\Core\Plugin\BaseHandler;
 use Manticoresearch\Buddy\Core\Task\Task;
diff --git a/src/Payload.php b/src/Payload.php
index b170340..a35f201 100644
--- a/src/Payload.php
+++ b/src/Payload.php
@@ -8,7 +8,7 @@
   version. You should have received a copy of the GPL license along with this
   program; if you did not, you can find it at http://www.gnu.org/
 */
-namespace Manticoresearch\Buddy\Plugin\Template;
+namespace Manticoresearch\Buddy\Plugin\ShowHostname;

 use Manticoresearch\Buddy\Core\Network\Request;
 use Manticoresearch\Buddy\Core\Plugin\BasePayload;
  1. 最后一步,不要忘记通过在容器内运行 composer install 安装新的依赖项。

从 Manticore Search 到你的插件的请求流及返回

我们现在准备实现我们的逻辑了。但首先,让我们了解一下它是如何在内部处理的。

Manticore Buddy 请求到插件流

假设我们正在使用 MySQL 客户端向已启动的 searchd 进程发送 SHOW HOSTNAME 查询。Manticore Search 无法处理它并将其发送给 Buddy 等待响应。Buddy 可以返回确切的响应,该响应将简单地被代理回 MySQL 客户端。

一旦 Buddy 从 Manticore Search 收到查询,它就会解析该查询并对所有插件进行验证:首先是核心插件,然后是外部插件。为了执行此验证,它会执行 Payload::hasMatch($request) 方法,将请求传递给它,该请求包含查询和一些元数据,例如请求的端点、类型(JSON 或 SQL)等。

Payload 代表包含处理命令所需的所有数据的结构。如果插件的 PayloadhasMatch 方法中返回 true,意味着我们将使用此插件来处理请求。它是由 Request 创建的, 在这个阶段,我们需要解析查询,提取有价值的信息,并将其设置到我们的负载数据中,这些数据将传递给 Handler

Handler 是实际执行实现的命令的类。它在使用并行扩展的线程环境中执行此操作,而不会阻塞主服务器循环。有一个简单的方法 run,创建一个闭包,该闭包必须执行一个任务并将响应返回给 Manticore Search,然后再代理给用户。

在我们的情况下,我们只需要添加一个检查以支持 SHOW HOSTNAME 查询语法,然后编写 Handler 逻辑以调用 gethostname PHP 函数并将其作为响应返回。就这样!让我们从 Payload 开始。

实现 Payload

当你打开 src/Payload.php 时,你会发现只有两个方法:fromRequesthasMatch。我们已经用 TODO 注释标记了需要编辑的行,因此很容易找到我们需要做的事情。在我们的情况下,查询非常简单且没有灵活性,所以我们不需要更改 fromRequest 方法中的任何内容。我们可以直接删除 TODO 注释。我们需要做的就是更新静态函数 hasMatch()。在那里我们需要实现一个检查,以便查询严格匹配字符串 SHOW HOSTNAME,并且不区分大小写。一旦匹配,我们应该返回 true。否则,我们应该返回 false。这就是基础系统在我们收到一个显示主机名的查询时理解需要使用此插件的方式。

我们对 Payload 所做的最终差异应如下所示:

diff --git a/src/Payload.php b/src/Payload.php
index a35f201..bd0795e 100644
--- a/src/Payload.php
+++ b/src/Payload.php
@@ -26,7 +26,6 @@ final class Payload extends BasePayload {
   */
  public static function fromRequest(Request $request): static {
    $self = new static();
-   // TODO: add logic of parsing request into payload here
    // We just need to do something, but actually its' just for PHPstan
    $self->path = $request->path;
    return $self;
@@ -37,7 +36,6 @@ final class Payload extends BasePayload {
   * @return bool
   */
  public static function hasMatch(Request $request): bool {
-   // TODO: validate $request->payload and return true, if your plugin should handle it
-   return $request->payload === 'template';
+   return stripos($request->payload, 'show hostname') !== false;
  }
 }

我们完成了 Payload 的更改,可以继续处理 Handler 来实现实际逻辑。

实现 Handler

现在,让我们打开 src/Handler.php 并检查 TODO 标记,以及发生的简要描述。我们需要导航到 run 方法并更新它。

我们可以看到一个返回 TaskResult 的闭包,它是一个特殊的结果类型,用于包装标准 JSON 响应。

我们将包括一个 gethostname() 调用,并通过将其包装在将返回给客户端的 TaskResult 中来准备响应。最终的差异将如下所示:

diff --git a/src/Handler.php b/src/Handler.php
index 07fcfd0..944899b 100644
--- a/src/Handler.php
+++ b/src/Handler.php
@@ -11,6 +11,7 @@
 namespace Manticoresearch\Buddy\Plugin\ShowHostname;

 use Manticoresearch\Buddy\Core\Plugin\BaseHandler;
+use Manticoresearch\Buddy\Core\Task\Column;
 use Manticoresearch\Buddy\Core\Task\Task;
 use Manticoresearch\Buddy\Core\Task\TaskResult;
 use RuntimeException;
@@ -33,9 +34,11 @@ final class Handler extends BaseHandler {
   * @throws RuntimeException
   */
  public function run(Runtime $runtime): Task {
-   // TODO: your logic goes into closure and should return TaskResult as response
    $taskFn = static function (): TaskResult {
-     return TaskResult::none();
+     $hostname = gethostname();
+     return TaskResult::withRow([
+       'hostname' => $hostname,
+     ])->column('hostname', Column::String);
    };

    return Task::createInRuntime(

调试和开发

现在你已经完成了实现,是时候测试和调试(如有必要)你的插件了。有一个特殊类型的插件称为 local,专门为此目的而设计。由于你的插件已经位于 plugins 目录中,因此它自动成为一个 local 插件。要使用它,只需:

  1. 在 Buddy 根目录中,运行命令 composer require [your-plugin-name]:dev-main 来包含该插件。your-plugin-name 是你在之前编辑的插件的 composer.json 中的名称。
  2. 重新启动 searchd 以确保代码已更新。

此方法允许你实时开发、调试、编辑和测试你的插件,而无需将其推送到 Git 存储库并在 packagist.org 上发布。

发布和安装

当你完成调试并准备发布你的插件时,只需将更改提交到 Git 存储库,并在 packagist.org 上发布你的包。

然后,通过运行 CREATE PLUGIN [your-plugin-name] TYPE 'buddy' VERSION 'dev-main' 命令来检查它是否按预期工作,该命令将尝试使用 Composer 下载插件并将其安装到 plugin_dir

像这样:

$ docker exec -it manticore-buddy mysql -h0 -P9306
MySQL [(none)]> CREATE PLUGIN manticoresoftware/buddy-plugin-show-hostname type 'buddy' VERSION 'v1.x';

Query OK, 0 rows affected (1 min 12.213 sec)

MySQL [(none)]> show hostname;
+----------+
| hostname |
+----------+
| dev      |
+----------+
1 row in set (0.011 sec)

MySQL [(none)]>

恭喜!你的插件运行良好,它允许你通过使用 SHOW hostname 查询获取主机名。

结论

我们希望您喜欢这篇文章,并对新的 Buddy 可插拔架构有了更深入的了解,该架构已在 Linux 开发包 中提供,并即将发布到下一个版本(在 Manticore 6.0.4 之后,因此如果您在阅读本文时有更新版本,则很可能已经包含在内)。您可以立即通过遵循我们提供的说明来开始开发您的插件。虽然这是对可插拔系统的基本介绍,但我们计划创建并发布一篇关于开发复杂插件的更高级文章。请随时在我们的 SlackTelegram 聊天中向我们提问,并随时建议复杂插件教程的主题。

请记住,查看我们的 核心插件代码 也是一个好主意,以更深入地了解它在更复杂情况下的工作原理。

诚挚的,
Manticore 团队。

安装Manticore Search

安装Manticore Search