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

教程
为了让您更轻松地开始开发自己的插件,让我们看看如何为 Buddy 构建一个简单的插件,该插件支持 SHOW HOSTNAME 命令并返回当前主机机器的主机名。这将帮助您理解创建自己插件时应遵循的过程,而且这是一个非常简单的示例。如果我们在实现其逻辑之前检查 SHOW HOSTNAME 在 Manticore Search 中的功能,我们可以期待得到如下结果:
mysql> SHOW HOSTNAME;
ERROR 1064 (42000): sphinxql: syntax error, unexpected identifier, expecting VARIABLES near 'HOSTNAME'
准备
我们首先应该做的是准备我们的开发环境。您只需执行一次此操作,之后可以从
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 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]开头。当您想停止该进程时,只需使用 Ctrl + C。
此时,一切都已准备好为 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)]>
You've just sent the command SHOW QUERIES to Manticore Search, which was then routed to Buddy and executed successfully. You can see what Buddy received and its response in the searchd log in the first terminal tab. So, we can confirm that Buddy is functioning correctly and everything has been set up right.
插件模板
We've created a
special GitHub template repository
that we highly recommend using to create any Manticore Buddy plugin. All plugins should be prefixed with buddy-plugin-[your-name], where [your-name] is the name of your plugin. For example, since we're developing a hostname selection plugin, we'll name it buddy-plugin-show-hostname. So:
- Open https://github.com/manticoresoftware/buddy-plugin-template .
- Choose
v1.xbranch - Click
Use this templateand thenCreate a new repository. - Fill out the form with your repository's name and create it. Make sure your repo name starts with
buddy-plugin-as it’s mandatory. git cloneyour new repo to thepluginsdirectory of the Manticore Buddy repo you previously deployed. Note that it's better to do this outside the container, especially if you're cloning from a private repository.
In
https://github.com/manticoresoftware/buddy-plugin-show-hostname
(branch: v1.x), you can find the entire task implemented. If you don’t want to delve into the details, you can simply git clone that instead of your forked repo. However, if you do want to master creating a plugin, let's continue.
添加真实代码
Now we need to follow the next steps and update our template with the related data to our new plugin.
- First, we open
composer.jsonand update the name of the plugin, description, and namespaces. So finally, your changes will look like this (just make sure you use your plugin repo name):
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": [
- We also need to update the namespaces of two classes –
PayloadandHandler. The diff should look like this:
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;
- As a final step, do not forget to install the new dependency by running
composer installinside the container.
从 Manticore Search 到你的插件再返回的请求流程
We are ready to implement our logic now. But first, let’s learn a little bit more about how it’s handled internally.

Let's assume we're sending a SHOW HOSTNAME query to the launched searchd process using a MySQL client. Manticore Search can't handle it and sends it to Buddy to await a response. Buddy can return the exact response, which will simply be proxied back to the MySQL client.
Once Buddy receives the query from Manticore Search, it parses it and runs validation across all plugins: first core plugins, and then external plugins. To run this validation, it executes the Payload::hasMatch($request) method, passing the request to it, which contains the query and some metadata, such as the requested endpoint, type (JSON or SQL), and so on.
Payload represents the structure containing all required data for handling the command. If a plugin's Payload returns true in the hasMatch method, it means that we will use this plugin to handle the request. It's created from the Request, and at this stage, we need to parse the query, extract valuable information, and set it to our payload data, which will be passed to the Handler.
Handler is the class that actually executes the implemented command. It does so in a threaded environment using the parallel extension without blocking the main server loop. There's a simple method run that creates a closure, which must perform a task and return the response back to Manticore Search, which is then proxied to the user.
In our case, we just need to add a check for supporting the SHOW HOSTNAME query syntax and then write the Handler logic to call the gethostname PHP function and return it as a response. That's it! Let's start with the Payload.
实现 Payload
When you open src/Payload.php, you will see there are just two methods: fromRequest and hasMatch. We have marked the lines we need to edit with TODO comments, so it should be easy to find what we need to do. In our case, the query is very simple and not flexible at all, so we don’t have to change anything in the fromRequest method. We can just remove the TODO comments. What we need to do is update the static function hasMatch(). There we need to implement a check that the query strictly matches the string SHOW HOSTNAME in a case-insensitive way. Once it has a match, we should return true. Otherwise, we should return false. That's how the base system understands that it needs to use this plugin in case we receive a query which shows a hostname.
The final diff we made to the Payload should look like this:
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;
}
}
We are done with the Payload changes and can move on to the Handler to implement the real logic.
实现 Handler
Now, let's open src/Handler.php and examine the TODO marks, along with a brief description of what's happening. We need to navigate to the run method and update it.
We can see a closure that returns a TaskResult, which is a special result type that wraps a standard JSON response.
We will include a gethostname() call and prepare the response by wrapping it within the TaskResult that will be returned to the client. The final diff will appear as follows:
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(
调试和开发
Now that you've completed the implementation, it's time to test and debug (if necessary) your plugin. There is a special type of plugin called local designed specifically for this purpose. Since your plugin is already in the plugins directory, it automatically becomes a local plugin. To use it, simply:
- 在根 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 '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 之后,因此如果您阅读时有更新版本,它可能已经包含在内)。您可以立即按照我们提供的说明开始开发您的插件。虽然这是对可插拔系统的基本介绍,但我们计划创建并发布一篇关于开发复杂插件的更高级文章。请随时在我们的 Slack 或 Telegram 聊天中向我们提问,并随时建议复杂插件教程的主题。
请记住,查看我们的 核心插件代码 也是一个好主意,以深入了解它在更复杂情况下的工作原理。
诚挚的,
Manticore 团队。
