zoukankan      html  css  js  c++  java
  • MySQL:如何编写daemon plugin (zhuan)

    mysql 

    1.什么是DaemonPlugin

    顾名思义,daemon plugin就是一种用来在后台运行的插件,在插件中,我们可以创建一些后台线程来做些有趣的事情。大名鼎鼎的handlesocket就是一个daemon plugin。而在mysql5.6中,也是通过daemon plugin来实现了memcached功能。

    2.为什么使用DaemonPlugin

    就像handlersocket,大胆的想象力能够创造无限的可能。MySQL Plugin的诱人之处在于其与Mysqld处于同一进程空间中,可以利用任何mysql内核的函数。Handlersocket在实现时,构造出相关参数并直接调用存储引擎的接口,从而穿越了语法解析和优化部分,对于逻辑简单的查询而言,可以极大的提高效率。

    另外所有的plugin都提供了showstatus和show variables 命令的接口,因此我们可以利用plugin来显示一些我们想要的信息,例如mysql内部的全局变量值。

    总的来说,daemon plugin可以做到以下几点:

    1) 创建后台线程,扩展mysql功能

    2)扩展status和variables信息

    Deamon plugin在mysqld启动时进行初始化,执行完init函数, 因此并不适用于与服务器进行通信的情形,mysql也没有提供任何相关的API

    3.如何编写daemonplugin

    这里涉及到的一些结构体,对其他类型的plugin而言也是通用的

    1)st_mysql_plugin

    无论声明哪种plugin,至少要包含该结构体

    字段

    类型

    描述

    type

    int

    用于描述plugin的类型,随着版本更新,越来越多,在5.5中包含8种类型:

    MYSQL_UDF_PLUGIN

    MYSQL_STORAGE_ENGINE_PLUGIN

    MYSQL_FTPARSER_PLUGIN

    MYSQL_DAEMON_PLUGIN

    MYSQL_INFORMATION_SCHEMA_PLUGIN

    MYSQL_AUDIT_PLUGIN

    MYSQL_REPLICATION_PLUGIN

    MYSQL_AUTHENTICATION_PLUGIN

    info

    void*

    用于指向特定的plugin描述符结构体,在daemon plugin中结构体为st_mysql_daemon,一般第一个字段都是插件接口的版本号

    name

    const char*

    plugin的名字,需要和安装时的名字一致

    author

    const char*

    plugin的作者信息,会在i_s.plugins表中显示

    descry

    const char*

    描述插件

    license

    ubt

    插件许可证:PLUGIN_LICENSE_PROPRIETARY

    PLUGIN_LICENSE_GPL

    PLUGIN_LICENSE_BSD

    init

    int (*init)(void *)

    当插件被加载时或者mysqld重启时会执行该函数,一般我们会在这里创建好后台线程

    deinit

    int (*deinit)(void *);

    当插件被卸载时做的工作,例如取消线程,清理内存等

    version

    unsigned int

    plugin的版本信息

    status_vars

    st_mysql_show_var*

    描述在执行show status时显示的信息

    system_vars

    st_mysql_sys_var **

    描述在执行show variables显示的信息

    __reserved1

    void*

    注释说为检查依赖而保留,不太明白,直接设为NULL即可

    flags

    unsigned long

    5.5之后增加的字段,plugin的flag:0、

    PLUGIN_OPT_NO_INSTALL(不可动态加载)、PLUGIN_OPT_NO_UNINSTALL(不可动态加载)

    在plugin.h里提供了宏,我们可以通过宏来声明插件:

    mysql_declare_plugin(my_plugin)

    {},

    {},

    ……

    mysql_declare_plugin_end;

    在两个宏之间,我们可以声明多个插件,也就是说在一个文件里,我们可以定义多个Plugin。

    上面提到三个结构体,需要在plugin里单独进行定义:

    a. st_mysql_daemon

    该结构体只包含一个字段,用于声明daemon plugin的版本信息

    字段

    类型

    描述

    interface_version

    int

    一般值为

    MYSQL_DAEMON_INTERFACE_VERSION

    也许有同学注意到了,这上面提到了两个version,即st_mysql_plugin里的version和st_mysql_daemon里的version,这两者是不相同的。

    st_mysql_plugin.version记录的是该plugin的版本号,使用16进制表示,低8位存储副版本号,其他存储主版本号。

    而st_mysql_daemon里存储的是daemonplugin接口的版本号,针对不同的mysql版本,其接口可能会发生变化。

    b. st_mysql_show_var

    结构体如下:

    字段

    类型

    描述

    name

    const char*

    字段名

    value

    char*

    字段值,我们可以将指针绑定到一个全局变量上

    type

    enum enum_mysql_show_type

    数据类型,包括(括号里对应value的指针类型):

    SHOW_BOOL (bool *)

    SHOW_INT   (unsigned int *)

    SHOW_LONG (long *)

    SHOW_LONGLONG (long long*)

    SHOW_DOUBLE (double *)

    SHOW_CHAR   (char *)

    SHOW_CHAR_PTR (char **)

    SHOW_ARRAY(st_mysql_show_var * )

    SHOW_FUNC(

    int (*)(MYSQL_THD, struct st_mysql_show_var*, char *) )

    该结构体用于定义show status时显示的值,可以看出在type字段最后两个相对其他比较特殊。

    当type类型为SHOW_ARRAY时,表明name字段并不是一个值,而是指向一个st_mysql_show_var类型的数组,数组以{0,0,0}结束,当前元素的name会成为引用数组元素name的前缀。

    当type类型为SHOW_FUNC时,value值为一个函数指针,参数包括当前线程的THD,st_mysql_show_var* 以及一个大小为1024字节的内存区域头指针;函数的目的是为了填充第二个字段的值,而buf作为存储构建结构体的内存空间;这样可以允许我们先做一些计算,然后显示计算的结果。

    c. st_mysql_sys_var

    该结构体内包含一个宏MYSQL_PLUGIN_VAR_HEADER,包含了变量结构体的公共部分。

    在这里,MySQL巧妙的使用了C的宏定义,例如,当我们定义一个variable:

    struct st_mysql_sys_var* my_sysvars[]= {

    MYSQL_SYSVAR(my_var),

    NULL}

    展开MYSQL_SYSVAR看看:

    #define MYSQL_SYSVAR_NAME(name)mysql_sysvar_ ## name

    #define MYSQL_SYSVAR(name) \

     ((struct st_mysql_sys_var *)&(MYSQL_SYSVAR_NAME(name)))

    那么MYSQL_SYSVAR(my_var)被转换为:

    ((struct st_mysql_sys_var *)mysql_sysvar_my_var

    因此,在这之前,我们首先要先创建好结构体。针对不同的数据类型,提供了许多宏来创建,分为两种:一种以MYSQL_SYSVAR开头的全局变量(可以set global),另外一种是MYSQL_THDVAR开头的session变量

    MYSQL_SYSVAR_BOOL(name, varname, opt, comment, check, update, def)

    char

    MYSQL_SYSVAR_STR(name, varname, opt, comment, check, update, def)

    char*

    MYSQL_SYSVAR_INT(name, varname, opt, comment, check, update, def, min, max, blk)

    int

    MYSQL_SYSVAR_UINT(name, varname, opt, comment, check, update, def, min, max, blk)

    unsigned int

    MYSQL_SYSVAR_LONG(name, varname, opt, comment, check, update, def, min, max, blk)

    long

    MYSQL_SYSVAR_ULONG(name, varname, opt, comment, check, update, def, min, max, blk)

    unsigned long

    MYSQL_SYSVAR_LONGLONG(name, varname, opt, comment, check, update, def, min, max, blk)

    long long

    MYSQL_SYSVAR_ULONGLONG(name, varname, opt, comment, check, update, def, min, max, blk)

    unsigned long long

    MYSQL_SYSVAR_ENUM(name, varname, opt, comment, check, update, def, typelib)

    unsigned long

    MYSQL_SYSVAR_SET(name, varname, opt, comment, check, update, def, typelib)

    unsigned long long

    以下是session变量定义宏

    MYSQL_THDVAR_BOOL(name, opt, comment, check, update, def)

    char

    MYSQL_THDVAR_STR(name, opt, comment, check, update, def)

    char*

    MYSQL_THDVAR_INT(name, opt, comment, check, update, def, min, max, blk)

    int

    MYSQL_THDVAR_UINT(name, opt, comment, check, update, def, min, max, blk)

    unsigned int

    MYSQL_THDVAR_LONG(name, opt, comment, check, update, def, min, max, blk)

    long

    MYSQL_THDVAR_ULONG(name, opt, comment, check, update, def, min, max, blk)

    unsigned long

    MYSQL_THDVAR_LONGLONG(name, opt, comment, check, update, def, min, max, blk)

    long long

    MYSQL_THDVAR_ULONGLONG(name, opt, comment, check, update, def, min, max, blk)

    unsigned long long

    MYSQL_THDVAR_ENUM(name, opt, comment, check, update, def, typelib)

    unsigned long

    MYSQL_THDVAR_SET(name, opt, comment, check, update, def, typelib)

    unsigned long long

    宏中参数描述如下:

    参数名

    描述

    name

    变量名,通过show variables显示的变量名,

    varname

    C/C++变量,用来给name赋值

    opt

    变量选项:

    PLUGIN_VAR_READONLY (变量只读)

    PLUGIN_VAR_NOSYSVAR (不是系统变量,只能在启动时命令行加上)

    PLUGIN_VAR_NOCMDOPT (与上述相反)

    PLUGIN_VAR_NOCMDARG (命令行,必须没有参数)

    PLUGIN_VAR_RQCMDARG (命令行,不需有参数)

    PLUGIN_VAR_OPCMDARG (命令行,参数可选)

    PLUGIN_VAR_MEMALLOC (如果被设置,会分配内存存储字符串的值,否则只能通过命令行来分配)

    comment

    变量的描述信息

    check

    函数指针,用来检查参数是否有效,如果无效,则拒绝修改,函数原型为:

    int check(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, struct st_mysql_value *value);

    其中thd为当前线程,var是我们定义的系统变量,save指针用来存储数据,value是传递给函数的值,我们可以从value中获取值,并将其保存到save中。

    update

    函数指针,当发生更新时,可以调用该函数做一些额外处理,函数原型为:

    void update(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save);

    def

    变量的默认值

    min

    最小值 (min及以下两个需要为数值类型)

    max

    最大值

    blk

    块大小,变量值需要是blk的整数倍

    typelib

    当变量类型为ENUM和SET类型时,使用该结构体定义:st_typelib

    在check函数里,我们从var中提取出新的值,并存储到save指针中。在update函数中,我们可以从save指针提取出新值,st_mysql_value正是用于提取新值的结构体,成员为函数指针,如下:

    函数指针

    描述

    int (*value_type)(struct st_mysql_value *)

    用于获取值的类型:

    MYSQL_VALUE_TYPE_STRING 0

    MYSQL_VALUE_TYPE_REAL   1

    MYSQL_VALUE_TYPE_INT    2

    const char *(*val_str)(struct st_mysql_value *, char *buffer, int *length);

    获取字符串

    int (*val_int)(struct st_mysql_value *, long long *intbuf);

    获取整数

    例如:

    Int a ;

    Var->val_int(var, &a);

    一些系统变量允许为SET或者ENUM类型,这时候,需要通过额外的结构体st_typelib来定义:

    字段

    类型

    描述

    count

    unsigned int

    type_names里的元素个数

    name

    const char*

    plugin无需设置

    type_names

    const char**

    存储集合内的元素值

    type_lengths

    unsigned int*

    plugin无需设置

    举个简单的例子,比如我们想定义一个INT变量,该变量为只读类型,即不允许通过set命令修改,最大为1000000,最小为0 ,默认值为 256:

    Static int xx

    Static MYSQL_SYSVAR_INT(xx_var, xx , PLUGIN_VAR_READONLY , “a read onlyint var”, NULL, NULL,256, 0, 1000000, 10)

    例如,如果plugin的名字为name,则变量的全名为name_xx_var。我们可以将系统变量通过命令行来赋值,也可以写在配置文件中,变量名为name-xx-var,赋值必须能被10整除,否则将被mysql拒绝。

    定义一个枚举类型,session变量

    static const char *mode_names[] = {

    "NORMAL", "TURBO","SUPER", "HYPER", "MEGA"

    };

    static TYPELIB modes = { 5, NULL,mode_names, NULL };

    static MYSQL_THDVAR_ENUM(mode,PLUGIN_VAR_NOCMDOPT,

    "one of NORMAL, TURBO, SUPER, HYPER,MEGA",

    NULL, NULL, 0, &modes);

    该变量属于枚举类型,每个session拥有自己的值,并且可在运行时修改;注意,当为session变量时,我们需要通过THDVAR(thd,mode)这样一个宏来获取相应的变量值

    另外,对于Plugin中的系统变量无需加互斥锁,MySQL会自动给我们加上。

    实例:启动一个后台线程,每隔5秒监控当前进程的状态(记录到log中),使用系统变量来控制是否记录log,并在show status显示记录的次数

    #include <string.h> 

    #include <plugin.h> 

    #include<mysql_version.h> 

    #include <my_global.h> 

    #include <my_sys.h> 

    #include<sys/resource.h> 

    #include <sys/time.h> 

      

    #define MONITORING_BUFFER1024 

      

    /*以下三个变量在sql/mysqld.cc中声明,因此需要extern*/ 

    extern ulong thread_id;  //当前最大线程id 

    extern uint thread_count;  //当前线程数 

    extern ulong max_connections;//最大允许连接数 

      

    static pthread_tmonitoring_thread; //线程id 

    static int monitoring_file;         //日志文件fd 

      

    static my_bool monitor_state= 1;   //为1表示记录日志,为0则否 

    static ulong monitor_num     = 0;   //后台线程循环次数 

    static struct rusage usage;         

      

    /*创建系统变量,可以通过配置文件或set global来修改*/ 

    MYSQL_SYSVAR_BOOL(monitors_state,monitor_state, 

                  PLUGIN_VAR_OPCMDARG, 

                  "disable monitor  if 0,default TRUE", 

                  NULL, NULL, TRUE); 

      

    struct st_mysql_sys_var*vars_system_var[] = { 

            MYSQL_SYSVAR(monitors_state), 

                NULL 

    }; 

      

    /*创建status变量,可通过showstatus查看*/ 

    static structst_mysql_show_var sys_status_var[] = 

        {"monitor_num", (char *)&monitor_num, SHOW_LONG}, 

        {0, 0, 0} 

    }; 

      

    /*线程函数,后台线程启动后,会持续执行该函数*/ 

    pthread_handler_tmonitoring(void *p) 

        char buffer[MONITORING_BUFFER]; 

        char time_str[20]; 

      

    while(1) { 

      

    /*每隔5秒记录一次,我们也可以把5修改为一个可配置的系统变量*/ 

            sleep(5); 

            

            if (!monitor_state) 

                continue; 

      

            monitor_num++; 

    /*获取当前时间,mysql自有函数*/ 

            get_date(time_str, GETDATE_DATE_TIME,0); 

            snprintf(buffer, MONITORING_BUFFER,"%s: %u of %lu clients connected, " 

                    "%lu connections made\n", 

                    time_str, thread_count, 

                    max_connections, thread_id); 

      

    /*使用getrusage函数来获得当前进程的运行状态,具体man getrusage*/ 

            if (getrusage(RUSAGE_SELF, &usage)== 0){ 

                snprintf(buffer+strlen(buffer) , 

    MONITORING_BUFFER, "user time:%d,system time:%d," 

                                                    "maxrss:%d,ixrss:%d,idrss:%d," 

                                                    "isrss:%d, minflt:%d, majflt:%d," 

                                                     "nswap:%d,inblock:%d,oublock:%d," 

                                                    "msgsnd:%d, msgrcv:%d,nsignals:%d," 

                                                    "nvcsw:%d, nivcsw:%d\n", 

                           usage.ru_utime, 

                           usage.ru_stime, 

                           usage.ru_maxrss, 

                           usage.ru_ixrss, 

                           usage.ru_idrss, 

                           usage.ru_isrss, 

                           usage.ru_minflt, 

                           usage.ru_majflt, 

                           usage.ru_nswap, 

                           usage.ru_inblock, 

                           usage.ru_oublock, 

                           usage.ru_msgsnd, 

                           usage.ru_msgrcv, 

                           usage.ru_nsignals, 

                           usage.ru_nvcsw, 

                           usage.ru_nivcsw); 

                /*写入monitoring_file文件*/ 

                write(monitoring_file, buffer,strlen(buffer)); 

            } 

        } 

      

    /*系统启动或加载插件时时调用该函数,用于创建后台线程*/ 

    static int monitoring_plugin_init(void*p) 

        pthread_attr_t attr; 

        char monitoring_filename[FN_REFLEN]; 

        char buffer[MONITORING_BUFFER]; 

        char time_str[20]; 

         monitor_num = 0; 

        /*format the filename

         *The fn_format() function is designed tobuild a filename and path compatible

         with the current operating system given aset of parameters. More details on its

         functionality can be found inmysys/mf_format.c.

         * */ 

        fn_format(monitoring_filename,"monitor", "", ".log", 

                MY_REPLACE_EXT |MY_UNPACK_FILENAME); 

        

        unlink(monitoring_filename); 

        monitoring_file = open(monitoring_filename, 

                O_CREAT | O_RDWR, 0644); 

        

        if (monitoring_file < 0) 

        { 

            fprintf(stderr, "Plugin'monitoring': " 

                    "Could not create file '%s'\n", 

                    monitoring_filename); 

            return 1; 

        } 

      

        get_date(time_str, GETDATE_DATE_TIME, 0); 

        sprintf(buffer, "Monitoring started at%s\n", time_str); 

        write(monitoring_file, buffer,strlen(buffer)); 

      

        pthread_attr_init(&attr); 

        pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE); 

        

        if (pthread_create(&monitoring_thread,&attr, 

                    monitoring, NULL) != 0){ 

      

            fprintf(stderr, "Plugin'monitoring': " 

                    "Could not create monitoringthread!\n"); 

            return 1; 

        } 

      

        return 0; 

      

    /*卸载插件时调用*/ 

    static intmonitoring_plugin_deinit(void *p) 

        char buffer[MONITORING_BUFFER]; 

    char time_str[20]; 

      

    /*通知后台线程结束*/ 

        pthread_cancel(monitoring_thread); 

        pthread_join(monitoring_thread, NULL); 

      

        get_date(time_str, GETDATE_DATE_TIME, 0); 

        sprintf(buffer, "Monitoring stopped at%s\n", time_str); 

        write(monitoring_file, buffer,strlen(buffer)); 

        close(monitoring_file); 

      

        return 0; 

      

    struct st_mysql_daemonmonitoring_plugin = { MYSQL_DAEMON_INTERFACE_VERSION }; 

      

      

    /*声明插件*/ 

    mysql_declare_plugin(monitoring) 

        MYSQL_DAEMON_PLUGIN, 

        &monitoring_plugin, 

        "monitoring", 

        "yinfeng", 

        "a daemon montor,log process usagestate", 

        PLUGIN_LICENSE_GPL, 

        monitoring_plugin_init, 

        monitoring_plugin_deinit, 

        0x0100, 

        sys_status_var, 

        vars_system_var, 

        NULL 

    mysql_declare_plugin_end; 

      

  • 相关阅读:
    配置Xmanager远程登录
    如何为ubuntu等Linux系统扩容(LVM)
    CentOS7配置iptables防火墙
    Cetnos搭建vsftp服务器
    vim设置
    zabbix 问题汇总
    转:常用验证正则表达式
    C#对象序列化成XML,以及自定义标签名
    改变FileUpload文件上传控件的显示方式,确认后上传
    改变FileUpload文件上传控件的显示方式,选择文件后自动上传
  • 原文地址:https://www.cnblogs.com/hitwtx/p/2322435.html
Copyright © 2011-2022 走看看