c++完成mysql 异步客户端

57 篇文章 1 订阅

如何用c++完成一个mysql 的客户端?

代码还需完善,但是核心代码在这里了

参考资料:

MySQL :: MySQL 8.0 C API Developer Guide :: 5.4.22 mysql_fetch_row()

注意几个点:

异步接口:

mysql_real_connect_nonblocking
mysql_real_query_nonblocking
mysql_store_result_nonblocking
mysql_fetch_row_nonblocking

配合异步io,epoll 可以很轻松完成,异步返回结果

/* state of an asynchronous operation */
enum net_async_status {
  NET_ASYNC_COMPLETE = 0,
  NET_ASYNC_NOT_READY,
  NET_ASYNC_ERROR,
  NET_ASYNC_COMPLETE_NO_MORE_RESULTS
};

简单上以下代码:

#pragma once

#include <memory>
#include <queue>
#include <functional>

#include <mysql/mysql.h>

#include "non_copyable.h"
#include "non_moveable.h"
#include "mysql_conn_config.h"
#include "mysql_message.h"
enum State{
    CONNECT_START,
    CONNECT_WAITING,
    CONNECT_DONE,

    QUERY_START,
    QUERY_WAITING,
    QUERY_RESULT_READY,

    RESULT_START,
    RESULT_WAITING,

    FETCH_ROW_START,
    FETCH_ROW_WAITING,
    FETCH_ROW_RESULT_READY,

    CLOSE_START,
    CLOSE_WAITING,
    CLOSE_DONE
};

namespace Event {
class EventLoop;
class EventChannel;
}

namespace Mysql {
class MysqlConnection :public Core::Noncopyable {
public:
    MysqlConnection(MysqlConnectionConfig& config);

    void start(const std::shared_ptr<Event::EventLoop>& loop);

    void query(const char* query, int index);


    ~MysqlConnection();

private:
    bool connect(const std::shared_ptr<Event::EventLoop>& loop);

    void onWrite(const Event::EventChannel* channel);

    void onRead(const Event::EventChannel* channel);

    void onClose(const Event::EventChannel* channel);

    void onError(const Event::EventChannel* channel);

    void nextStep(int status, int state_wait, const std::function<void()>& onWait, int state_go_on, const std::function<void()>& onContinue);

    void onResult();

    void onQuery();

    void onQueryFinish();

    int current_state = CONNECT_START;                   // State machine current state
    MYSQL mysql;
    MYSQL *ret;
    MYSQL_RES *result;
    MYSQL_ROW row;
    std::queue<MysqlMessage> current_query_entry;
    int err = 0;
    int index = 0;
    MysqlConnectionConfig mysql_config;
    struct timeval now;
};
}

实现文件

#include "mysql/mysql_connection.h"

#include <iostream>
#include "build_expect.h"
#include "os/unix_logger.h"
#include "mysql/mysql_response.h"
#include "event/event_no_buffer_channel.h"

namespace Mysql {
MysqlConnection::MysqlConnection(MysqlConnectionConfig &config) {
    mysql_init(&mysql);
    mysql_config = config;
    mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "async_queries");

    // set timeouts to 300 microseconds
    uint default_timeout = config.getDefaultTimeout();
    mysql_options(&mysql, MYSQL_OPT_READ_TIMEOUT, &default_timeout);
    mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &default_timeout);
    mysql_options(&mysql, MYSQL_OPT_WRITE_TIMEOUT, &default_timeout);
    current_state = CONNECT_START;
}

void MysqlConnection::start(const std::shared_ptr<Event::EventLoop>& loop) {
    connect(loop);
}

void MysqlConnection::query(const char *query, int index_) {
    MysqlMessage message;
    message.query = query;
    message.index = index_;
    current_query_entry.push(message);
}

void MysqlConnection::nextStep(int status, int state_wait, const std::function<void()>& onWait, int state_go_on, const std::function<void()>& onContinue) {
    if (status == NET_ASYNC_ERROR) {
        SYSTEM_ERROR_LOG_TRACE(std::to_string(mysql_errno(&mysql)) + ":" + (mysql_error(&mysql)))
        return;
    }

    if (status == NET_ASYNC_NOT_READY) {
        current_state = state_wait;
        onWait();
        return;
    }

    current_state = state_go_on;
    onContinue();
}

void MysqlConnection::onResult() {
    net_async_status status = mysql_store_result_nonblocking(&mysql, &result);
    nextStep(status, RESULT_WAITING, []{}, FETCH_ROW_START, [this]{
        onQueryFinish();
    });
}

void MysqlConnection::onQuery() {
    if (build_unlikely(current_query_entry.empty())) {
        current_state = CLOSE_START;
        return;
    }

    auto message = current_query_entry.front();

    if (message.index == 0) {
        gettimeofday(&now, nullptr);  // start time
    }

    net_async_status status = mysql_real_query_nonblocking(&mysql, message.query.c_str(), message.query.length());

    nextStep(status, QUERY_WAITING, []{}, FETCH_ROW_WAITING, [this]{
        onResult();
    });
}

void MysqlConnection::onQueryFinish() {
    MysqlResponse response(result);
    while (row) {
        net_async_status status = mysql_fetch_row_nonblocking(result, &row);
        if (!row) {
            break;
        }
        nextStep(status, FETCH_ROW_WAITING, [] {}, FETCH_ROW_RESULT_READY,
                 [this, &response] {
            response.addRow(row);
        });
    }
}

void MysqlConnection::onRead(const Event::EventChannel *channel) {
    switch (current_state) {
        case CONNECT_WAITING: {
            net_async_status status = mysql_real_connect_nonblocking(&mysql, mysql_config.getHost().c_str(), mysql_config.getUser().c_str(),
                 mysql_config.getPassword().c_str(), mysql_config.getDB().c_str(), mysql_config.getPort(), nullptr, 0);

            nextStep(status, CONNECT_WAITING, []{}, CONNECT_DONE, [this]{
                onQuery();
            });
            break;
        }

        case CONNECT_DONE: {
            nextStep(NET_ASYNC_COMPLETE, CONNECT_DONE, []{}, QUERY_START, [this]{
                onQuery();
            });
            break;
        }

        case QUERY_START: {
            onQuery();
            break;
        }

        case QUERY_WAITING: {
            onQuery();
            break;
        }

        case RESULT_START: {
            onResult();
            break;
        }

        case RESULT_WAITING: {
            onResult();
            break;
        }

        case FETCH_ROW_START: {
            onQueryFinish();
            break;
        }


        case FETCH_ROW_WAITING: {
            onQueryFinish();
            break;
        }
    }
}

void MysqlConnection::onWrite(const Event::EventChannel *channel) {

}

void MysqlConnection::onClose(const Event::EventChannel *channel) {

}

void MysqlConnection::onError(const Event::EventChannel *channel) {

}

bool MysqlConnection::connect(const std::shared_ptr<Event::EventLoop>& loop) {
    net_async_status status = mysql_real_connect_nonblocking(&mysql, mysql_config.getHost().c_str(), mysql_config.getUser().c_str(),
    mysql_config.getPassword().c_str(), mysql_config.getDB().c_str(), mysql_config.getPort(), nullptr, 0);
    if (status == NET_ASYNC_ERROR) {
        std::cout << mysql_error(&mysql) << std::endl;
        return false;
    }

    if (NET_ASYNC_NOT_READY == status) {
        current_state = CONNECT_WAITING;
    } else {
        current_state = CONNECT_DONE;
    }

    std::shared_ptr<Event::EventNoBufferChannel> channel = std::make_shared<Event::EventNoBufferChannel>(loop, mysql.net.fd);
    channel->setOnWriteCallable(std::bind(&MysqlConnection::onWrite, this, std::placeholders::_1));
    channel->setOnReadCallable(std::bind(&MysqlConnection::onRead, this, std::placeholders::_1));
    channel->setOnErrorCallable(std::bind(&MysqlConnection::onError, this, std::placeholders::_1));
    channel->setOnCloseCallable(std::bind(&MysqlConnection::onClose, this, std::placeholders::_1));
    channel->enable(-1);
    return true;
}

MysqlConnection::~MysqlConnection() {
    mysql_close(&mysql);
}
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值