Так получается, что сейчас тружусь над планировщиком для MySQL соединений. И тут недавно пришлось покапаться в документации/блогах и т.д. И вот решил поделиться с сообществом как реализовать асинхронные запросы к MySQL серверу на С++ используя API и библиотеку libmysqlclient.
Для того, чтобы начать программировать с использованием API MySQL, нам нужен заголовочный файл mysql.h.
И библиотека libmysqlclient. Для deb-подобных ОС всё это можно поставить так:
Компилятору указываем следующее:
-L/path/to/mysqliclientlib/ -lmysqlclient
Если пишите код в Qt Creator, то в файле проекта можно добавить следующее(у вас может быть другой путь):
LIBS += -L/usr/lib/mysql -lmysqlclient
У нас почти всё готово, для того чтобы работать с MySQL на С++.
Определяемся: асинхронные запросы — несколько параллельных запросов к базе без использования потоков (thread). Используя функцию mysql_real_query этого добиться невозможно, т.е. программа будет ожидать результат(ответ) от сервера.
Кроме всего прочего, я использую библиотеку libevent для отлова событий. Поставить её можно так:
Указываем компилятору:
LIBS += -L/usr/lib/mysql -lmysqlclient -L/usr/lib/ -levent
Заголовочный файл:
#include <event.h>
Всё, всё готово. Приступаем. Итак пишем класс который будет посылать запросы асинхронно. У меня этот класс содержит ещё методы — я остановлюсь только на тех, которые имеют отошение к теме.
В конструкторе класса (или где-то ещё) инициализируем событийную базу:
Далее нам будет необходимо установить соединения с сервером и «запомнить» их. Бежим в цикле по заданном числу запросов и сохраняем соединения в векторе v.
Осталось выполнить нужные нам запросы по этим коннекшнам:
На этом почти всё. Осталось почистить за собой:
И последнее — функция обработки результата запроса. Она имеет вид:
На этом всё. Надеюсь кому-то пригодится.
p.s. сильно не пинать — в C++ не гуру.
Для того, чтобы начать программировать с использованием API MySQL, нам нужен заголовочный файл mysql.h.
И библиотека libmysqlclient. Для deb-подобных ОС всё это можно поставить так:
sudo apt-get install libmysqlclient-dev libmysqlclient
Компилятору указываем следующее:
-L/path/to/mysqliclientlib/ -lmysqlclient
Если пишите код в Qt Creator, то в файле проекта можно добавить следующее(у вас может быть другой путь):
LIBS += -L/usr/lib/mysql -lmysqlclient
У нас почти всё готово, для того чтобы работать с MySQL на С++.
Определяемся: асинхронные запросы — несколько параллельных запросов к базе без использования потоков (thread). Используя функцию mysql_real_query этого добиться невозможно, т.е. программа будет ожидать результат(ответ) от сервера.
Кроме всего прочего, я использую библиотеку libevent для отлова событий. Поставить её можно так:
sudo apt-get install libevent-dev libevent
Указываем компилятору:
LIBS += -L/usr/lib/mysql -lmysqlclient -L/usr/lib/ -levent
Заголовочный файл:
#include <event.h>
Всё, всё готово. Приступаем. Итак пишем класс который будет посылать запросы асинхронно. У меня этот класс содержит ещё методы — я остановлюсь только на тех, которые имеют отошение к теме.
class CBalanceMySQL { private: // ваши приватные члены // число одновременных процессов unsigned int thread_count; // соединения с базой std::vector<MYSQL*> v; // события std::vector<event*> events; // запросы std::vector<std::string> queries; struct event_base *evbase; // указатель на функцию, при успешном выполнении запросы и // ответа от сервера БД pFunc p_func; // метод для выполнения запросов асинхронно void run(); public: // constructor CBalanceMySQL(); // destructor ~CBalanceMySQL(); // Тут ваши остальные публичные методы // устанавливаем число процессов одновременных void setThreadCount(int); // соединяемся с базой bool ConnectAllThreads(); // даём указатель на функцию в случае успеха bool setSuccessFunc(void(*)(int, short, void*)); };
В конструкторе класса (или где-то ещё) инициализируем событийную базу:
this->evbase = event_base_new();
Далее нам будет необходимо установить соединения с сервером и «запомнить» их. Бежим в цикле по заданном числу запросов и сохраняем соединения в векторе v.
bool CBalanceMySQL::ConnectAllThreads() { for (uint i = 0; i < this->thread_count; i++) { MYSQL *m; m = mysql_init(NULL); if (!mysql_real_connect(m, this->host, this->user, this->password, NULL, this->port, NULL, 0)) { std::cout << mysql_error(m); return 0; } else { this->v.push_back(m); } } return 1; }
Осталось выполнить нужные нам запросы по этим коннекшнам:
void CBalanceMySQL::run() { for (uint i = 0; i < this->v.size(); i++) { // событие struct event *ev; // запрос к конкретному коннекту std::string q = this->queries.at(i); // ныжный нам коннект MYSQL *m = this->v.at(i); // выполняем запрос mysql_send_query(m, q.c_str() , strlen(q.c_str())); ev = new event; // регистрируем событие // по ответу - вызывается функция по указанному адресу p_func event_set(ev, m->net.fd, EV_READ, this->p_func, m); event_base_set(this->evbase, ev); event_add(ev, NULL); events.push_back(ev); } // цикл до тех пор пока события не зарегистрируются while (0 == event_base_loop(this->evbase, 0)); }
На этом почти всё. Осталось почистить за собой:
for (uint i = 0; i < v.size(); i++) { mysql_close(this->v.at(i)); } for (uint i = 0; i < events.size(); i++) { delete(this->events.at(i)); } event_base_free(this->evbase);
И последнее — функция обработки результата запроса. Она имеет вид:
static void read_result(int fd, short event, void *_userdata) { MYSQL *m = (MYSQL*)_userdata; if (0 == mysql_read_query_result(m)) { MYSQL_RES *res = mysql_store_result(m); MYSQL_ROW row = mysql_fetch_row(res); // у меня был запрос SELECT COUNT(*) FROM information_schema.processlist, я просто вывожу число соединений cout << "cnt for net.fd = " << fd << " is " << row[0] << endl; mysql_free_result(res); } }
На этом всё. Надеюсь кому-то пригодится.
p.s. сильно не пинать — в C++ не гуру.
