这篇文章给大家介绍MySQL中如何编写daemon plugin,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
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,至少要包含该结构体
在plugin.h里提供了宏,我们可以通过宏来声明插件:
mysql_declare_plugin(my_plugin)
{},
{},
……
mysql_declare_plugin_end;
在两个宏之间,我们可以声明多个插件,也就是说在一个文件里,我们可以定义多个Plugin。
上面提到三个结构体,需要在plugin里单独进行定义:
a. st_mysql_daemon
该结构体只包含一个字段,用于声明daemon plugin的版本信息
也许有同学注意到了,这上面提到了两个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
结构体如下:
该结构体用于定义show status时显示的值,可以看出在type字段最后两个相对其他比较特殊。
当type类型为SHOW_ARRAY时,表明name字段并不是一个值,而是指向一个st__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变量
宏中参数描述如下:
在check函数里,我们从var中提取出新的值,并存储到save指针中。在update函数中,我们可以从save指针提取出新值,st_mysql_value正是用于提取新值的结构体,成员为函数指针,如下:
例如:
Int a ;
Var->val_int(var, &a);
一些系统变量允许为SET或者ENUM类型,这时候,需要通过额外的结构体st_typelib来定义:
举个简单的例子,比如我们想定义一个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;
关于MySQL中如何编写daemon plugin就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。