嘿,大家好。关于 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 ,并具有基本逻辑以使一切正常工作并找到正确的插件以在命令时启动。
为了更清楚地理解该过程,这里有一个图示说明请求 → 响应流:

教程
为了让您更轻松地开始开发自己的插件,让我们看看如何为 Buddy 构建一个简单的插件,该插件支持 SHOW HOSTNAME 命令并返回当前主机机器的主机名。这将帮助您理解在创建自己的插件时应遵循的过程,而且这是一个非常简单的示例。如果我们在实现其逻辑之前检查 SHOW HOSTNAME 在 Manticore Search 中的功能,我们可以期待得到如下结果:
mysql> SHOW HOSTNAME;
ERROR 1064 (42000): sphinxql: syntax error, unexpected identifier, expecting VARIABLES near 'HOSTNAME'
准备
我们首先应该做的是准备我们的开发环境。您只需执行一次此操作,之后可以从
GIT repository
更新 Buddy 以接收最新更新。让我们打开终端,将 Buddy 克隆到名为 manticoresearch-buddy 的文件夹中,并导航到克隆的文件夹。
git clone --depth 1 https://github.com/manticoresoftware/manticoresearch-buddy.git
cd manticoresearch-buddy
现在您拥有 Buddy 的整个源代码,但没有安装任何包。Buddy 使用 Composer 来管理其依赖项,我们需要先安装它们才能运行应用程序。但是,在此之前,我们将运行一个专门为开发准备的特殊 Docker 容器。Manticore Executor Kit 镜像包含所有必要的工具,以便顺利进行 Manticore Buddy 开发体验,无论您是在创建新插件还是为 Buddy 本身做贡献。
docker pull ghcr.io/manticoresoftware/manticoresearch:test-kit-latest
docker create --privileged --entrypoint bash \
-v $(pwd):/workdir -w /workdir --name manticore-buddy \
--network host -it ghcr.io/manticoresoftware/manticoresearch:test-kit-latest
docker start manticore-buddy
恭喜!您现在已经启动了一个带有 Manticore Search 和 Manticore Executor 的 Docker 容器,以开发 Buddy 和插件。您当前所在的工作目录映射到容器内的 /workdir。您的下一步是安装 Composer 依赖项。请记住,此命令及所有后续命令必须在 manticore-buddy 容器内执行。
docker exec -it manticore-buddy bash
composer install
您的 Buddy 现在已准备好用于使用、开发、调试和测试。接下来,您需要完成两件事:
- 首先,在容器内编辑
/etc/manticoresearch/manticore.conf并添加buddy_path,将我们的源代码放在那里。这将让 Manticore Search 知道我们需要使用从源代码运行 Buddy 的自定义路径,而不是从已安装的模块中。将此添加到searchd部分:buddy_path = manticore-executor /workdir/src/main.php --debug - 其次,尝试运行
searchd(Manticore Search 服务器),确保它正常工作。您应该看到几行以# 在容器内启动守护进程并保持在前台 searchd --nodetach[BUDDY]开头的日志。
此时,一切都已准备好为 Buddy 准备开发环境并开始实现新插件。您仍然可以进行实验并尝试执行一些查询,以确保其正常运行。例如,打开另一个终端窗口并运行以下命令:
$ docker exec -it manticore-buddy mysql -h0 -P9306
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 6.0.5 3bcbd00fa@230320 dev (columnar 2.0.5 8171c1a@230320) (secondary 2.0.5 8171c1a@230320) git branch HEAD (no branch)
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
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)]>
您刚刚向 Manticore Search 发送了命令 SHOW QUERIES,该命令随后被路由到 Buddy 并成功执行。您可以在第一个终端标签中的 searchd 日志中查看 Buddy 收到的内容及其响应。因此,我们可以确认 Buddy 正在正常工作,一切都已正确设置。
当您想要停止 searchd 进程时,只需在第一个终端窗口/标签中按 Ctrl + C。
插件模板
我们创建了一个
特殊的 GitHub 模板库
,我们强烈建议您使用它来创建任何 Manticore Buddy 插件。所有插件的前缀应为 buddy-plugin-[your-name],其中 [your-name] 是您的插件名称。例如,由于我们正在开发一个主机名选择插件,我们将其命名为 buddy-plugin-show-hostname。所以:
- 打开 https://github.com/manticoresoftware/buddy-plugin-template 。
- 点击
Use this template然后Create a new repository。 - 填写表单,输入您的库名称并创建它。确保您的库名称以
buddy-plugin-开头,因为这是强制性的。 git clone您的新库到您之前部署的 Manticore Buddy 库的plugins目录。请注意,最好在容器外执行此操作,特别是如果您是从私有库克隆。
在
https://github.com/manticoresoftware/buddy-plugin-show-hostname
中,您可以找到整个任务的实现。如果您不想深入了解细节,您可以简单地 git clone 那个而不是您的分叉库。然而,如果您确实想掌握创建插件的技巧,让我们继续。
添加真实代码
现在我们需要按照下一步更新我们的模板,以便与我们的新插件相关的数据。
- 首先,在您克隆的插件目录中打开
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": [
- 我们还需要更新您插件中两个类的命名空间 -
Payload和Handler。差异应如下所示:
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;
- 作为最后一步,请不要忘记通过在容器内(在
/workdir中)运行composer install来安装新依赖项。
从 Manticore Search 到您的插件再到 Manticore Search 的请求流
我们现在准备实现我们的逻辑。但首先,让我们多了解一下它是如何在内部处理的。

假设我们使用 MySQL 客户端向启动的 searchd 进程发送 SHOW HOSTNAME 查询。Manticore Search 无法处理它,并将其发送给 Buddy 以等待响应。Buddy 可以返回确切的响应,这将简单地代理回 MySQL 客户端。
一旦 Buddy 从 Manticore Search 收到查询,它会解析它并对所有插件进行验证:首先是核心插件,然后是外部插件。为了运行此验证,它执行 Payload::hasMatch($request) 方法,将请求传递给它,该请求包含查询和一些元数据,例如请求的端点、类型(JSON 或 SQL)等。
Payload 代表包含处理命令所需的所有数据的结构。如果插件的 Payload 在 hasMatch 方法中返回 true,这意味着我们将使用此插件来处理请求。它是从 Request 创建的,在这个阶段,我们需要解析查询,提取有价值的信息,并将其设置为我们的有效负载数据,这将传递给 Handler。
Handler 是实际执行实现命令的类。它在使用并行扩展的线程环境中执行此操作,而不会阻塞主服务器循环。有一个简单的方法 run,它创建一个闭包,必须执行一个任务并将响应返回给 Manticore Search,然后再代理给用户。
在我们的案例中,我们只需要添加一个检查,以支持 SHOW HOSTNAME 查询语法,然后编写 Handler 逻辑以调用 gethostname PHP 函数并将其作为响应返回。就这样!让我们从 Payload 开始。
实现 Payload
当您打开 src/Payload.php 时,您会看到只有两个方法:fromRequest 和 hasMatch。我们已经用 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(): 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::create(
调试和开发
现在您已经完成了实现,是时候测试和调试(如果需要)您的插件了。有一种特殊类型的插件称为 local,专门为此目的而设计。由于您的插件已经在 plugins 目录中,因此它自动成为一个 local 插件。要使用它,只需:
- 在根 Buddy 目录中,运行命令
composer require [your-plugin-name]:dev-main以包含该插件。your-plugin-name是您之前编辑的插件composer.json中的名称。 - 重启
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 'dev-main';
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 查询来获取主机名。
结论
我们希望您喜欢这篇文章,并对自 Manticore 版本 6.0.4 以来可用的新 Buddy 可插拔架构有更深入的了解。您可以立即按照我们提供的说明开始开发您的插件。虽然这是对可插拔系统的基本介绍,但我们计划创建并发布一篇关于开发复杂插件的更高级文章。请随时在我们的 Slack 或 Telegram 聊天中向我们提问,并随时建议复杂插件教程的主题。
请记住,探索 Buddy 的 内置插件代码 是个好主意,以便更深入地了解它在更复杂场景中的工作原理。
诚挚的,
Manticore 团队。
