扩展 MySQL 9.0  /  ...  /  使用您自己的协议跟踪插件

4.4.11.2 使用您自己的协议跟踪插件

注意

要使用您自己的协议跟踪插件,您必须使用 WITH_TEST_TRACE_PLUGIN CMake 选项(禁用)来配置 MySQL,因为一次只能加载一个协议跟踪插件,并且在尝试加载第二个插件时会发生错误。如果您已经构建了启用了测试协议跟踪插件的 MySQL 以了解其工作原理,则必须在不使用它构建 MySQL 才能使用您自己的插件。

本节讨论如何编写一个名为 simple_trace 的基本协议跟踪插件。此插件提供了一个框架,展示了如何设置客户端插件描述符并创建与跟踪相关的回调函数。在 simple_trace 中,这些函数是基本的,除了说明所需的论据外,几乎没有其他作用。要详细了解跟踪插件如何使用跟踪事件信息,请检查测试协议跟踪插件的源文件(MySQL 源代码发行版中 libmysql 目录下的 test_trace_plugin.cc)。但是,请注意,此处使用的 st_mysql_client_plugin_TRACE 结构与通常的客户端插件声明宏所使用的结构不同。特别是,前两个成员是显式定义的,而不是由声明宏隐式定义的。

几个头文件包含与协议跟踪插件相关的信息

  • client_plugin.h:定义客户端插件的 API。这包括客户端插件描述符和客户端插件 C API 调用的函数原型(请参阅 C API 客户端插件接口)。

  • plugin_trace.h:包含 MYSQL_CLIENT_TRACE_PLUGIN 类型的客户端插件的声明。它还包含对允许的协议阶段、阶段之间的转换以及每个阶段允许的事件类型的描述。

要编写协议跟踪插件,请在插件源文件中包含以下头文件。根据插件的功能和要求,也可能需要其他 MySQL 或通用头文件。

#include <mysql/plugin_trace.h>
#include <mysql.h>

plugin_trace.h 包含 client_plugin.h,因此您无需显式包含后者。

使用 mysql_declare_client_plugin()mysql_end_client_plugin 宏声明客户端插件描述符(请参阅 第 4.4.2.3 节,“客户端插件描述符”)。对于 simple_trace 插件,描述符如下所示

mysql_declare_client_plugin(TRACE)
  "simple_trace",                 /* plugin name */
  "Author Name",                  /* author */
  "Simple protocol trace plugin", /* description */
  {1,0,0},                        /* version = 1.0.0 */
  "GPL",                          /* license type */
  NULL,                           /* for internal use */
  plugin_init,                    /* initialization function */
  plugin_deinit,                  /* deinitialization function */
  plugin_options,                 /* option-handling function */
  trace_start,                    /* start-trace function */
  trace_stop,                     /* stop-trace function */
  trace_event                     /* event-handling function */
mysql_end_client_plugin;

从插件名称到选项处理函数的描述符成员对于所有客户端插件类型都是通用的。通用成员之后的成员实现跟踪事件处理。

插件不需要处理的函数成员可以在描述符中声明为 NULL,在这种情况下,您不需要编写任何相应的函数。为了便于说明并显示参数语法,以下讨论实现了描述符中列出的所有函数,即使其中一些函数什么也不做。

所有客户端插件通用的初始化、反初始化和选项函数声明如下。有关参数和返回值的描述,请参阅 第 4.4.2.3 节,“客户端插件描述符”

static int
plugin_init(char *errbuf, size_t errbuf_len, int argc, va_list args)
{
  return 0;
}

static int
plugin_deinit()
{
  return 0;
}

static int
plugin_options(const char *option, const void *value)
{
  return 0;
}

客户端插件描述符的跟踪特定成员是回调函数。以下描述提供了有关如何使用它们的更多详细信息。每个函数都有一个第一个参数,它是一个指向插件实例的指针,以防您的实现需要访问它。

trace_start():此函数在每个跟踪连接的开始(插件加载后启动的每个连接)调用。它传递连接处理程序和跟踪开始的协议阶段。 trace_start() 分配 trace_event() 函数所需的内存(如果有),并返回一个指向它的指针。如果不需要内存,则此函数返回 NULL

static void*
trace_start(struct st_mysql_client_plugin_TRACE *self,
            MYSQL *conn,
            enum protocol_stage stage)
{
  struct st_trace_data *plugin_data= malloc(sizeof(struct st_trace_data));

  fprintf(stderr, "Initializing trace: stage %d\n", stage);
  if (plugin_data)
  {
    memset(plugin_data, 0, sizeof(struct st_trace_data));
    fprintf(stderr, "Trace initialized\n");
    return plugin_data;
  }
  fprintf(stderr, "Could not initialize trace\n");
  exit(1);
}

trace_stop():此函数在连接跟踪结束时调用。这通常发生在连接关闭时,但也可能更早发生。例如,trace_event() 可以随时返回一个非零值,这会导致连接跟踪终止。然后,即使连接尚未结束,也会调用 trace_stop()

trace_stop() 传递连接处理程序和一个指向 trace_start() 分配的内存的指针(如果没有,则为 NULL)。如果指针不是 NULL,则 trace_stop() 应释放内存。此函数不返回值。

static void
trace_stop(struct st_mysql_client_plugin_TRACE *self,
           MYSQL *conn,
           void *plugin_data)
{
  fprintf(stderr, "Terminating trace\n");
  if (plugin_data)
    free(plugin_data);
}

trace_event():每次事件发生时都会调用此函数。它传递一个指向 trace_start() 分配的内存的指针(如果没有,则为 NULL)、连接处理程序、当前协议阶段和事件代码以及事件数据。此函数返回 0 以继续跟踪,如果跟踪应停止,则返回非零值。

static int
trace_event(struct st_mysql_client_plugin_TRACE *self,
            void *plugin_data,
            MYSQL *conn,
            enum protocol_stage stage,
            enum trace_event event,
            struct st_trace_event_args args)
{
  fprintf(stderr, "Trace event received: stage %d, event %d\n", stage, event);
  if (event == TRACE_EVENT_DISCONNECTED)
    fprintf(stderr, "Connection closed\n");
  return 0;
}

当连接结束时,跟踪框架会关闭连接的跟踪,因此只有当您想提前终止连接的跟踪时,trace_event() 才应返回非零值。假设您只想跟踪某个 MySQL 帐户的连接。身份验证后,您可以检查连接的用户名,如果不是您感兴趣的用户,则停止跟踪。

对于每次调用 trace_event()st_trace_event_args 结构都包含事件数据。它有以下定义

struct st_trace_event_args
{
  const char           *plugin_name;
  int                   cmd;
  const unsigned char  *hdr;
  size_t                hdr_len;
  const unsigned char  *pkt;
  size_t                pkt_len;
};

对于不同的事件类型,st_trace_event_args 结构包含以下信息。所有长度均以字节为单位。未使用的成员设置为 0/NULL

AUTH_PLUGIN 事件

plugin_name  The name of the plugin

SEND_COMMAND 事件

cmd          The command code
hdr          Pointer to the command packet header
hdr_len      Length of the header
pkt          Pointer to the command arguments
pkt_len      Length of the arguments

其他 SEND_xxxxxx_RECEIVED 事件

pkt          Pointer to the data sent or received
pkt_len      Length of the data

PACKET_SENT 事件

pkt_len      Number of bytes sent

要编译和安装插件库文件,请使用 第 4.4.3 节,“编译和安装插件库” 中的说明。要使库文件可供使用,请将其安装在插件目录(plugin_dir 系统变量命名的目录)中。

在插件库文件编译并安装到插件目录后,您可以通过将 LIBMYSQL_PLUGINS 环境变量设置为插件名称来轻松测试它,这会影响任何使用该变量的客户端程序。 mysql 就是这样的程序之一

$> export LIBMYSQL_PLUGINS=simple_trace
shqll> mysql
Initializing trace: stage 0
Trace initialized
Trace event received: stage 0, event 1
Trace event received: stage 0, event 2
...
Welcome to the MySQL monitor.  Commands end with ; or \g.
Trace event received
Trace event received
...
mysql> SELECT 1;
Trace event received: stage 4, event 12
Trace event received: stage 4, event 16
...
Trace event received: stage 8, event 14
Trace event received: stage 8, event 15
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

mysql> quit
Trace event received: stage 4, event 12
Trace event received: stage 4, event 16
Trace event received: stage 4, event 3
Connection closed
Terminating trace
Bye

要停止加载跟踪插件,请执行以下操作

$> LIBMYSQL_PLUGINS=

也可以编写直接加载插件的客户端程序。您可以通过调用 mysql_options() 设置 MYSQL_PLUGIN_DIR 选项来告诉客户端插件目录所在的位置

char *plugin_dir = "path_to_plugin_dir";

/* ... process command-line options ... */

mysql_options(&mysql, MYSQL_PLUGIN_DIR, plugin_dir);

通常,程序还会接受一个 --plugin-dir 选项,允许用户覆盖默认值。

如果客户端程序需要更低级别的插件管理,则客户端库包含采用 st_mysql_client_plugin 参数的函数。请参阅 C API 客户端插件接口