溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

MySQL中如何編寫daemon plugin

發(fā)布時間:2021-07-26 11:05:51 來源:億速云 閱讀:158 作者:Leah 欄目:數(shù)據(jù)庫

這篇文章給大家介紹MySQL中如何編寫daemon plugin,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

1.什么是DaemonPlugin

顧名思義,daemon plugin就是一種用來在后臺運行的插件,在插件中,我們可以創(chuàng)建一些后臺線程來做些有趣的事情。大名鼎鼎的handlesocket就是一個daemon plugin。而在mysql5.6中,也是通過daemon plugin來實現(xiàn)了memcached功能。

2.為什么使用DaemonPlugin

就像handlersocket,大膽的想象力能夠創(chuàng)造無限的可能。MySQL Plugin的誘人之處在于其與Mysqld處于同一進(jìn)程空間中,可以利用任何mysql內(nèi)核的函數(shù)。Handlersocket在實現(xiàn)時,構(gòu)造出相關(guān)參數(shù)并直接調(diào)用存儲引擎的接口,從而穿越了語法解析和優(yōu)化部分,對于邏輯簡單的查詢而言,可以極大的提高效率。

另外所有的plugin都提供了showstatus和show variables 命令的接口,因此我們可以利用plugin來顯示一些我們想要的信息,例如mysql內(nèi)部的全局變量值。

總的來說,daemon plugin可以做到以下幾點:

1) 創(chuàng)建后臺線程,擴展mysql功能

2)擴展status和variables信息

Deamon plugin在mysqld啟動時進(jìn)行初始化,執(zhí)行完init函數(shù), 因此并不適用于與服務(wù)器進(jìn)行通信的情形,mysql也沒有提供任何相關(guān)的API

3.如何編寫daemonplugin

這里涉及到的一些結(jié)構(gòu)體,對其他類型的plugin而言也是通用的

1)st_mysql_plugin

無論聲明哪種plugin,至少要包含該結(jié)構(gòu)體

在plugin.h里提供了宏,我們可以通過宏來聲明插件:

mysql_declare_plugin(my_plugin)

{},

{},

……

mysql_declare_plugin_end;

在兩個宏之間,我們可以聲明多個插件,也就是說在一個文件里,我們可以定義多個Plugin。

上面提到三個結(jié)構(gòu)體,需要在plugin里單獨進(jìn)行定義:

a. st_mysql_daemon

該結(jié)構(gòu)體只包含一個字段,用于聲明daemon plugin的版本信息

也許有同學(xué)注意到了,這上面提到了兩個version,即st_mysql_plugin里的version和st_mysql_daemon里的version,這兩者是不相同的。

st_mysql_plugin.version記錄的是該plugin的版本號,使用16進(jìn)制表示,低8位存儲副版本號,其他存儲主版本號。

而st_mysql_daemon里存儲的是daemonplugin接口的版本號,針對不同的mysql版本,其接口可能會發(fā)生變化。

b. st_mysql_show_var

結(jié)構(gòu)體如下:

該結(jié)構(gòu)體用于定義show status時顯示的值,可以看出在type字段最后兩個相對其他比較特殊。

當(dāng)type類型為SHOW_ARRAY時,表明name字段并不是一個值,而是指向一個st__show_var類型的數(shù)組,數(shù)組以{0,0,0}結(jié)束,當(dāng)前元素的name會成為引用數(shù)組元素name的前綴。

當(dāng)type類型為SHOW_FUNC時,value值為一個函數(shù)指針,參數(shù)包括當(dāng)前線程的THD,st_mysql_show_var* 以及一個大小為1024字節(jié)的內(nèi)存區(qū)域頭指針;函數(shù)的目的是為了填充第二個字段的值,而buf作為存儲構(gòu)建結(jié)構(gòu)體的內(nèi)存空間;這樣可以允許我們先做一些計算,然后顯示計算的結(jié)果。

c. st_mysql_sys_var

該結(jié)構(gòu)體內(nèi)包含一個宏MYSQL_PLUGIN_VAR_HEADER,包含了變量結(jié)構(gòu)體的公共部分。

在這里,MySQL巧妙的使用了C的宏定義,例如,當(dāng)我們定義一個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)被轉(zhuǎn)換為:

((struct st_mysql_sys_var *)mysql_sysvar_my_var

因此,在這之前,我們首先要先創(chuàng)建好結(jié)構(gòu)體。針對不同的數(shù)據(jù)類型,提供了許多宏來創(chuàng)建,分為兩種:一種以MYSQL_SYSVAR開頭的全局變量(可以set global),另外一種是MYSQL_THDVAR開頭的session變量

宏中參數(shù)描述如下:

在check函數(shù)里,我們從var中提取出新的值,并存儲到save指針中。在update函數(shù)中,我們可以從save指針提取出新值,st_mysql_value正是用于提取新值的結(jié)構(gòu)體,成員為函數(shù)指針,如下:

例如:

Int a ;

Var->val_int(var, &a);

一些系統(tǒng)變量允許為SET或者ENUM類型,這時候,需要通過額外的結(jié)構(gòu)體st_typelib來定義:

舉個簡單的例子,比如我們想定義一個INT變量,該變量為只讀類型,即不允許通過set命令修改,最大為1000000,最小為0 ,默認(rèn)值為 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。我們可以將系統(tǒng)變量通過命令行來賦值,也可以寫在配置文件中,變量名為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擁有自己的值,并且可在運行時修改;注意,當(dāng)為session變量時,我們需要通過THDVAR(thd,mode)這樣一個宏來獲取相應(yīng)的變量值

另外,對于Plugin中的系統(tǒng)變量無需加互斥鎖,MySQL會自動給我們加上。

實例:啟動一個后臺線程,每隔5秒監(jiān)控當(dāng)前進(jìn)程的狀態(tài)(記錄到log中),使用系統(tǒng)變量來控制是否記錄log,并在show status顯示記錄的次數(shù)

#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;  //當(dāng)前最大線程id 

extern uint thread_count;  //當(dāng)前線程數(shù) 

extern ulong max_connections;//最大允許連接數(shù) 

static pthread_tmonitoring_thread; //線程id 

static int monitoring_file;         //日志文件fd 

static my_bool monitor_state= 1;   //為1表示記錄日志,為0則否 

static ulong monitor_num     = 0;   //后臺線程循環(huán)次數(shù) 

static struct rusage usage;         

/*創(chuàng)建系統(tǒng)變量,可以通過配置文件或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 

}; 

/*創(chuàng)建status變量,可通過showstatus查看*/ 

static structst_mysql_show_var sys_status_var[] = 

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

    {0, 0, 0} 

}; 

/*線程函數(shù),后臺線程啟動后,會持續(xù)執(zhí)行該函數(shù)*/ 

pthread_handler_tmonitoring(void *p) 

    char buffer[MONITORING_BUFFER]; 

    char time_str[20]; 

while(1) { 

/*每隔5秒記錄一次,我們也可以把5修改為一個可配置的系統(tǒng)變量*/ 

        sleep(5); 

        if (!monitor_state) 

            continue; 

        monitor_num++; 

/*獲取當(dāng)前時間,mysql自有函數(shù)*/ 

        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函數(shù)來獲得當(dāng)前進(jìn)程的運行狀態(tài),具體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)); 

        } 

    } 

/*系統(tǒng)啟動或加載插件時時調(diào)用該函數(shù),用于創(chuàng)建后臺線程*/ 

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; 

/*卸載插件時調(diào)用*/ 

static intmonitoring_plugin_deinit(void *p) 

    char buffer[MONITORING_BUFFER]; 

char time_str[20]; 

/*通知后臺線程結(jié)束*/ 

    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; 

關(guān)于MySQL中如何編寫daemon plugin就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI