自从ProxySQL1.4.4, 开始将历史metric信息存入新的数据库proxysql_stats.db. 这些信息主要包括connection_pool,connection,query_cache,system_cpu,system_memory这几部分。在上一篇文章:ProxySQL-Docker化部署共享数据目录 中,我介绍了这个功能给ProxySQL docker化部署共享数据目录所带来的问题。即:多个ProxySQL在采集系统信息存入proxysql_stats.db 时经常发生冲突,日志中会有大量报错!上文中也提到,这些信息主要是为内置的web http server提供数据,然而即使web_enabled=’false’,不启用web http server时,这些信息也会采集入库。这显得有些多余。我也向Rene反应了这一问题,希望有个开关能控制这一行为。但最近许多问题Rene都没有回复,估计也是烦了,本来issue是提bug的地方,现在却被用来免费问答,人家也再三说明这一点,希望通过提供comercial support来解决你的问题,不知道有多少用户会请求commercial support. 辛勤的劳动成果却得不到应有的回报,谁也会失去耐心!

好吧,我只好静下心来翻代码了!

采集系统信息存入proxysql_stats.db的操作,主要在文件ProxySQL_Statistics.cpp 中, 下面是这个类中的几个方法:

1
2
3
4
5
6
7
8
get_mysql_metrics[ProxySQL_Statistics]

get_MySQL_Query_Cache_metrics [ProxySQL_Statistics]
get_system_memory_metrics [ProxySQL_Statistics]
get_system_cpu_metrics [ProxySQL_Statistics]
system_cpu_sets [ProxySQL_Statistics]
system_memory_sets [ProxySQL_Statistics]
MySQL_Threads_Handler_sets [ProxySQL_Statistics]

其中以get开头的几个方法是从proxysql_stats.db中查询数据提供给web http server,以供前端展示,而后面几个…sets则是采集数据存入proxysql_stats.db中,其采集周期有相关变量设定。只要找到调用它们的地方就好办了。

经过一番折腾,发现调用它们的地方在ProxySQL_Admin.cpp 的静态函数 admin_main_loop中,下面是这个函数中的一段代码(3201行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
__end_while_pool:
{
if (GloProxyStats->MySQL_Threads_Handler_timetoget(curtime)) {
if (GloMTH) {
SQLite3_result * resultset=GloMTH->SQL3_GlobalStatus(false);
if (resultset) {
GloProxyStats->MySQL_Threads_Handler_sets(resultset);
delete resultset;
}
}
}
if (GloProxyStats->MySQL_Query_Cache_timetoget(curtime)) {
if (GloQC) {
SQLite3_result * resultset=GloQC->SQL3_getStats();
if (resultset) {
GloProxyStats->MySQL_Query_Cache_sets(resultset);
delete resultset;
}
}
}
if (GloProxyStats->system_cpu_timetoget(curtime)) {
GloProxyStats->system_cpu_sets();
}
#ifndef NOJEM
if (GloProxyStats->system_memory_timetoget(curtime)) {
GloProxyStats->system_memory_sets();
}
#endif
}

经过测试,把这几行代码注释掉,就不会再采集系统信息入库了。但这个方法有些简单粗暴。最好是使用一个变量来开启/关闭这一行为。我设想的是使用变量admin-web_enalbe变量,它默认是false, 为true时,说明启用web http , 这种情况下是有必要开启这一功能的,不然前端展示不出数据。而为false时,完全可以关闭这一功能。

继续挖代码发现,这个静态函数在ProxySQL_Admin类的init() 方法中被用来创建来一个线程 (No.3667)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//      pthread_t admin_thr;
struct _main_args *arg=(struct _main_args *)malloc(sizeof(struct _main_args));
arg->nfds=main_poll_nfds;
arg->fds=main_poll_fds;
arg->shutdown=&main_shutdown;
arg->callback_func=main_callback_func;
if (pthread_create(&admin_thr, &attr, admin_main_loop, (void *)arg) !=0 ) {
perror("Thread creation");
exit(EXIT_FAILURE);
}
do { usleep(50); } while (__sync_fetch_and_sub(&load_main_,0)==0);
load_main_=0;
#ifdef DEBUG
std::cerr << "Admin initialized in ";
#endif

这个init方法在ProxySQL启动时main函数中会调用。尝试取消启动这个线程,发现有问题,端口无法绑定,ProxySQL启动异常。好吧,这一块就不动了。还得从admin_main_loop函数中入手。

如果能在__end_while_pool标签中,判断一些admin-web_enabled是否为true,就能轻易的解决。那个如何在这里访问admin-web_enabled这个变量呢?

又扒了一顿代码。发现Admin相关变量是在ProxySQL_Admin的构造函数中初始化的。

1
2
3
...
variables.web_enabled = false;
...

variables是个私有化结构体,不能在ProxySQL_Admin类外直接访问。admin_main_loop函数是静态函数,不属于任何类。而pthread使用它创建线程时传参是通过一个_main_args结构体来传的。

1
pthread_create(&admin_thr, &attr, admin_main_loop, (void *)arg) !=0

这个结构体是这样的:

1
2
3
4
5
6
7
typedef struct _main_args {
int nfds;
struct pollfd *fds;
int *callback_func;
volatile int *shutdown;

} main_args;

如果我们想在admin_main_loop函数中能够获取到变量admin-web_enabled,就需要传递一个ProxySQL_Admin 对象的指针。因此我在这个struct中添加了这样一个指针。

1
ProxySQL_Admin *p_admin=NULL:

然后在创建线程的地方,初始化该指针为this:

1
2
3
4
5
6
7
8
9
10
11
12
struct _main_args *arg=(struct _main_args *)malloc(sizeof(struct _main_args));
arg->nfds=main_poll_nfds;
arg->fds=main_poll_fds;
arg->shutdown=&main_shutdown;
//here
arg->p_admin=this;

arg->callback_func=main_callback_func;
if (pthread_create(&admin_thr, &attr, admin_main_loop, (void *)arg) !=0 ) {
perror("Thread creation");
exit(EXIT_FAILURE);
}

这样ProxySQL_Admin的指针就带进了方法admin_main_loop中。然后在把这个指针接过来就可以用了。

1
2
3
4
5
6
7
8
9
10
11
12
static void * admin_main_loop(void *arg)
{
int i;
int version=0;
struct sockaddr_in addr;
struct pollfd *fds=((struct _main_args *)arg)->fds;
int nfds=((struct _main_args *)arg)->nfds;
int *callback_func=((struct _main_args *)arg)->callback_func;
volatile int *shutdown=((struct _main_args *)arg)->shutdown;
//here
ProxySQL_Admin *padmin=((struct _main_args *)arg)->p_admin;
char *socket_names[MAX_ADMIN_LISTENERS];

在__end_while_pool标签代码块中判断admin-web_enabled是否为true,如果是就采集系统信息存入proxsql_stats.db中,否则这里什么也不做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
__end_while_pool:
{ //here
if (strcmp(padmin->get_variable((char *)"web_enabled"), (char *)"true") == 0){
if (GloProxyStats->MySQL_Threads_Handler_timetoget(curtime)) {
if (GloMTH) {
SQLite3_result * resultset=GloMTH->SQL3_GlobalStatus(false);
if (resultset) {
GloProxyStats->MySQL_Threads_Handler_sets(resultset);
delete resultset;
}
}
}
if (GloProxyStats->MySQL_Query_Cache_timetoget(curtime)) {
if (GloQC) {
SQLite3_result * resultset=GloQC->SQL3_getStats();
if (resultset) {
GloProxyStats->MySQL_Query_Cache_sets(resultset);
delete resultset;
}
}
}
if (GloProxyStats->system_cpu_timetoget(curtime)) {
GloProxyStats->system_cpu_sets();
}
#ifndef NOJEM
if (GloProxyStats->system_memory_timetoget(curtime)) {
GloProxyStats->system_memory_sets();
}
#endif
}
}

修改后编译部署,测试效果与预期相符!
我是不是该递交一个pull request看作者认不认可这种修改?!