嘿,大家好。关于 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。这是一个使用 Buddy Core 的 ReactPHP 应用程序,并具有使所有功能正常工作和找到正确插件以命令启动的基本逻辑。
为了更清楚地理解这一过程,这里有一个图示请求 → 响应流的示意图:
教程
为了让事情对你更简单,并深入开发自己的插件,让我们看看如何为 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
欢迎使用 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 行在集合中 (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 到您的插件的请求流程及其返回
我们现在准备实现我们的逻辑。但首先,让我们进一步了解它是如何在内部处理的。
假设我们通过 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 应该如下所示:
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 将如下所示:
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 团队。