Так получается, что сейчас тружусь над планировщиком для MySQL соединений. И тут недавно пришлось покапаться в документации/блогах и т.д. И вот решил поделиться с сообществом как реализовать асинхронные запросы к MySQL серверу на С++ используя API и библиотеку libmysqlclient.
Для того, чтобы начать программировать с использованием 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++ не гуру.
Автор: modestguy