diff --git a/config.example.json b/config.example.json index 36292fd3..5bfd803e 100644 --- a/config.example.json +++ b/config.example.json @@ -44,7 +44,10 @@ "user": "", //passwd: '' by default "passwd": "", - //connection_number: 1 by default + //is_fast: false by default, if it is true, the client is faster but user can't call + //any synchronous interface of it. + is_fast: false, + //connection_number: 1 by default, valid only if is_fast is false. "connection_number": 1 } ],*/ @@ -136,8 +139,6 @@ //idle_connection_timeout: Defaults to 60 seconds, the lifetime //of the connection without read or write "idle_connection_timeout": 60, - //enable_fast_db_client: Defaults to false - "enable_fast_db_client": false, //server_header_field: Set the 'server' header field in each response sent by drogon, //empty string by default with which the 'server' header field is set to "Server: drogon/version string\r\n" "server_header_field": "", diff --git a/drogon_ctl/templates/config.csp b/drogon_ctl/templates/config.csp index 36292fd3..5bfd803e 100644 --- a/drogon_ctl/templates/config.csp +++ b/drogon_ctl/templates/config.csp @@ -44,7 +44,10 @@ "user": "", //passwd: '' by default "passwd": "", - //connection_number: 1 by default + //is_fast: false by default, if it is true, the client is faster but user can't call + //any synchronous interface of it. + is_fast: false, + //connection_number: 1 by default, valid only if is_fast is false. "connection_number": 1 } ],*/ @@ -136,8 +139,6 @@ //idle_connection_timeout: Defaults to 60 seconds, the lifetime //of the connection without read or write "idle_connection_timeout": 60, - //enable_fast_db_client: Defaults to false - "enable_fast_db_client": false, //server_header_field: Set the 'server' header field in each response sent by drogon, //empty string by default with which the 'server' header field is set to "Server: drogon/version string\r\n" "server_header_field": "", diff --git a/lib/inc/drogon/HttpAppFramework.h b/lib/inc/drogon/HttpAppFramework.h index 7f492fd2..d7f7be8f 100755 --- a/lib/inc/drogon/HttpAppFramework.h +++ b/lib/inc/drogon/HttpAppFramework.h @@ -71,9 +71,7 @@ class HttpAppFramework : public trantor::NonCopyable /** * Calling this method starts the IO event loops and the main loop of the application; * Usually, the thread that calls this method is the main thread of the application; - * If the loop() method is called before this method, it must be called from the thread - * that first calls the loop () method. - * If all loop() calls are after this method, it can be called from any thread. + * This method blocks the calling thread until the main loop exits. */ virtual void run() = 0; @@ -188,7 +186,6 @@ class HttpAppFramework : public trantor::NonCopyable virtual void setPipelineRequestsNumber(const size_t number) = 0; #if USE_ORM virtual orm::DbClientPtr getDbClient(const std::string &name = "default") = 0; - virtual void enableFastDbClient() = 0; virtual orm::DbClientPtr getFastDbClient(const std::string &name = "default") = 0; virtual void createDbClient(const std::string &dbType, const std::string &host, @@ -198,7 +195,8 @@ class HttpAppFramework : public trantor::NonCopyable const std::string &password, const size_t connectionNum = 1, const std::string &filename = "", - const std::string &name = "default") = 0; + const std::string &name = "default", + const bool isFast = false) = 0; #endif private: diff --git a/lib/src/ConfigLoader.cc b/lib/src/ConfigLoader.cc index f7c0bf86..fec9b94c 100644 --- a/lib/src/ConfigLoader.cc +++ b/lib/src/ConfigLoader.cc @@ -228,14 +228,8 @@ static void loadApp(const Json::Value &app) //Kick off idle connections auto kickOffTimeout = app.get("idle_connection_timeout", 60).asUInt64(); drogon::app().setIdleConnectionTimeout(kickOffTimeout); -#if USE_ORM - //Fast db client - auto fastDbClient = app.get("enable_fast_db_client", false).asBool(); - if (fastDbClient) - drogon::app().enableFastDbClient(); -#endif auto server = app.get("server_header_field", "").asString(); - if(!server.empty()) + if (!server.empty()) drogon::app().setServerHeaderField(server); auto keepaliveReqs = app.get("keepalive_requests", 0).asUInt64(); drogon::app().setKeepaliveRequestsNumber(keepaliveReqs); @@ -263,7 +257,8 @@ static void loadDbClients(const Json::Value &dbClients) auto connNum = client.get("connection_number", 1).asUInt(); auto name = client.get("name", "default").asString(); auto filename = client.get("filename", "").asString(); - drogon::app().createDbClient(type, host, (u_short)port, dbname, user, password, connNum, filename, name); + auto isFast = client.get("is_fast", false).asBool(); + drogon::app().createDbClient(type, host, (u_short)port, dbname, user, password, connNum, filename, name, isFast); } #else std::cout << "No database is supported by drogon, please install the database development library first." << std::endl; diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 0d15f215..83ebd0fe 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -266,14 +266,6 @@ void HttpAppFrameworkImpl::run() _running = true; -#if USE_ORM - //Create db clients - for (auto const &fun : _dbFuncs) - { - fun(); - } -#endif - if (!_libFilePaths.empty()) { _sharedLibManagerPtr = std::unique_ptr(new SharedLibManager(loop(), _libFilePaths)); @@ -288,7 +280,7 @@ void HttpAppFrameworkImpl::run() { LOG_TRACE << "thread num=" << _threadNum; auto loopThreadPtr = std::make_shared("DrogonIoLoop"); - loopThreadPtr->run(); + //loopThreadPtr->run(); loopThreads.push_back(loopThreadPtr); ioLoops.push_back(loopThreadPtr->getLoop()); for (auto const &listener : _listeners) @@ -328,7 +320,7 @@ void HttpAppFrameworkImpl::run() { LOG_TRACE << "thread num=" << _threadNum; auto loopThreadPtr = std::make_shared("DrogonListeningLoop"); - loopThreadPtr->run(); + //loopThreadPtr->run(); loopThreads.push_back(loopThreadPtr); auto ip = std::get<0>(listener); bool isIpv6 = ip.find(":") == std::string::npos ? false : true; @@ -362,12 +354,12 @@ void HttpAppFrameworkImpl::run() serverPtr->kickoffIdleConnections(_idleConnectionTimeout); serverPtr->start(); /// Use std::promise to ensure that IO loops have been created - std::promise pro; - auto f = pro.get_future(); - serverPtr->getLoop()->runInLoop([&pro]() { - pro.set_value(1); - }); - f.get(); + // std::promise pro; + // auto f = pro.get_future(); + // serverPtr->getLoop()->runInLoop([&pro]() { + // pro.set_value(1); + // }); + // f.get(); auto serverIoLoops = serverPtr->getIoLoops(); for (auto serverIoLoop : serverIoLoops) { @@ -378,9 +370,7 @@ void HttpAppFrameworkImpl::run() #endif #if USE_ORM - // Create fast db clients for every io loop - if (_enableFastDbClient) - createFastDbClient(ioLoops); + createDbClients(ioLoops); #endif _httpCtrlsRouter.init(ioLoops); _httpSimpleCtrlsRouter.init(ioLoops); @@ -414,22 +404,56 @@ void HttpAppFrameworkImpl::run() } } _responseCachingMap = std::unique_ptr>(new CacheMap(loop(), 1.0, 4, 50)); //Max timeout up to about 70 days; - loop()->loop(); + + // Let listener event loops run when everything is ready. + for (auto &loopTh : loopThreads) + { + loopTh->run(); + } + _mainLoopThread.run(); + _mainLoopThread.wait(); } #if USE_ORM -void HttpAppFrameworkImpl::createFastDbClient(const std::vector &ioloops) +void HttpAppFrameworkImpl::createDbClients(const std::vector &ioloops) { - for (auto &iter : _dbClientsMap) + assert(_dbClientsMap.empty()); + assert(_dbFastClientsMap.empty()); + for (auto &dbInfo : _dbInfos) { - for (auto *loop : ioloops) + if (dbInfo._isFast) { - if (iter.second->type() == drogon::orm::ClientType::Sqlite3) + for (auto *loop : ioloops) { - _dbFastClientsMap[iter.first][loop] = iter.second; + if (dbInfo._dbType == drogon::orm::ClientType::Sqlite3) + { + LOG_ERROR << "Sqlite3 don't support fast mode"; + abort(); + } + if (dbInfo._dbType == drogon::orm::ClientType::PostgreSQL || dbInfo._dbType == drogon::orm::ClientType::Mysql) + { + _dbFastClientsMap[dbInfo._name][loop] = std::shared_ptr(new drogon::orm::DbClientLockFree(dbInfo._connectionInfo, loop, dbInfo._dbType)); + } } - if (iter.second->type() == drogon::orm::ClientType::PostgreSQL || iter.second->type() == drogon::orm::ClientType::Mysql) + } + else + { + if (dbInfo._dbType == drogon::orm::ClientType::PostgreSQL) { - _dbFastClientsMap[iter.first][loop] = std::shared_ptr(new drogon::orm::DbClientLockFree(iter.second->connectionInfo(), loop, iter.second->type())); +#if USE_POSTGRESQL + _dbClientsMap[dbInfo._name] = drogon::orm::DbClient::newPgClient(dbInfo._connectionInfo, dbInfo._connectionNumber); +#endif + } + else if (dbInfo._dbType == drogon::orm::ClientType::Mysql) + { +#if USE_MYSQL + _dbClientsMap[dbInfo._name] = drogon::orm::DbClient::newMysqlClient(dbInfo._connectionInfo, dbInfo._connectionNumber); +#endif + } + else if (dbInfo._dbType == drogon::orm::ClientType::Sqlite3) + { +#if USE_SQLITE3 + _dbClientsMap[dbInfo._name] = drogon::orm::DbClient::newSqlite3Client(dbInfo._connectionInfo, dbInfo._connectionNumber); +#endif } } } @@ -721,8 +745,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu trantor::EventLoop *HttpAppFrameworkImpl::loop() { - static trantor::EventLoop loop; - return &loop; + return _mainLoopThread.getLoop(); } HttpAppFramework &HttpAppFramework::instance() @@ -737,10 +760,13 @@ HttpAppFramework::~HttpAppFramework() #if USE_ORM orm::DbClientPtr HttpAppFrameworkImpl::getDbClient(const std::string &name) { + assert(_dbClientsMap.find(name) != _dbClientsMap.end()); return _dbClientsMap[name]; } orm::DbClientPtr HttpAppFrameworkImpl::getFastDbClient(const std::string &name) { + assert(_dbFastClientsMap[name].find(trantor::EventLoop::getEventLoopOfCurrentThread()) != + _dbFastClientsMap[name].end()); return _dbFastClientsMap[name][trantor::EventLoop::getEventLoopOfCurrentThread()]; } void HttpAppFrameworkImpl::createDbClient(const std::string &dbType, @@ -751,7 +777,8 @@ void HttpAppFrameworkImpl::createDbClient(const std::string &dbType, const std::string &password, const size_t connectionNum, const std::string &filename, - const std::string &name) + const std::string &name, + const bool isFast) { assert(!_running); auto connStr = utils::formattedString("host=%s port=%u dbname=%s user=%s", host.c_str(), port, databaseName.c_str(), userName.c_str()); @@ -762,13 +789,17 @@ void HttpAppFrameworkImpl::createDbClient(const std::string &dbType, } std::string type = dbType; std::transform(type.begin(), type.end(), type.begin(), tolower); + DbInfo info; + info._connectionInfo = connStr; + info._connectionNumber = connectionNum; + info._isFast = isFast; + info._name = name; + if (type == "postgresql") { #if USE_POSTGRESQL - _dbFuncs.push_back([this, connStr, connectionNum, name]() { - auto client = drogon::orm::DbClient::newPgClient(connStr, connectionNum); - _dbClientsMap[name] = client; - }); + info._dbType = orm::ClientType::PostgreSQL; + _dbInfos.push_back(info); #else std::cout << "The PostgreSQL is not supported by drogon, please install the development library first." << std::endl; exit(1); @@ -777,10 +808,8 @@ void HttpAppFrameworkImpl::createDbClient(const std::string &dbType, else if (type == "mysql") { #if USE_MYSQL - _dbFuncs.push_back([this, connStr, connectionNum, name]() { - auto client = drogon::orm::DbClient::newMysqlClient(connStr, connectionNum); - _dbClientsMap[name] = client; - }); + info._dbType = orm::ClientType::Mysql; + _dbInfos.push_back(info); #else std::cout << "The Mysql is not supported by drogon, please install the development library first." << std::endl; exit(1); @@ -790,10 +819,9 @@ void HttpAppFrameworkImpl::createDbClient(const std::string &dbType, { #if USE_SQLITE3 std::string sqlite3ConnStr = "filename=" + filename; - _dbFuncs.push_back([this, sqlite3ConnStr, connectionNum, name]() { - auto client = drogon::orm::DbClient::newSqlite3Client(sqlite3ConnStr, connectionNum); - _dbClientsMap[name] = client; - }); + info._connectionInfo = sqlite3ConnStr; + info._dbType = orm::ClientType::Sqlite3; + _dbInfos.push_back(info); #else std::cout << "The Sqlite3 is not supported by drogon, please install the development library first." << std::endl; exit(1); diff --git a/lib/src/HttpAppFrameworkImpl.h b/lib/src/HttpAppFrameworkImpl.h index 25540c52..24290c58 100644 --- a/lib/src/HttpAppFrameworkImpl.h +++ b/lib/src/HttpAppFrameworkImpl.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -94,7 +95,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework virtual void setPipelineRequestsNumber(const size_t number) override { _pipelineRequestsNumber = number; } size_t keepaliveRequestsNumber() const { return _keepaliveRequestsNumber; } size_t pipelineRequestsNumber() const { return _pipelineRequestsNumber; } - + virtual ~HttpAppFrameworkImpl() noexcept { //Destroy the following objects before _loop destruction @@ -124,10 +125,6 @@ class HttpAppFrameworkImpl : public HttpAppFramework #if USE_ORM virtual orm::DbClientPtr getDbClient(const std::string &name = "default") override; - virtual void enableFastDbClient() override - { - _enableFastDbClient = true; - } virtual orm::DbClientPtr getFastDbClient(const std::string &name = "default") override; virtual void createDbClient(const std::string &dbType, const std::string &host, @@ -137,7 +134,8 @@ class HttpAppFrameworkImpl : public HttpAppFramework const std::string &password, const size_t connectionNum = 1, const std::string &filename = "", - const std::string &name = "default") override; + const std::string &name = "default", + const bool isFast = false) override; #endif inline static HttpAppFrameworkImpl &instance() @@ -212,16 +210,24 @@ class HttpAppFrameworkImpl : public HttpAppFramework size_t _pipelineRequestsNumber = 0; bool _useSendfile = true; bool _useGzip = true; - bool _enableFastDbClient = false; int _staticFilesCacheTime = 5; std::unordered_map> _staticFilesCache; std::mutex _staticFilesCacheMutex; #if USE_ORM std::map _dbClientsMap; - std::vector> _dbFuncs; + struct DbInfo + { + std::string _name; + std::string _connectionInfo; + orm::ClientType _dbType; + bool _isFast; + size_t _connectionNumber; + }; + std::vector _dbInfos; std::map> _dbFastClientsMap; - void createFastDbClient(const std::vector &ioloops); + void createDbClients(const std::vector &ioloops); #endif + trantor::EventLoopThread _mainLoopThread; }; } // namespace drogon diff --git a/orm_lib/src/DbClientLockFree.cc b/orm_lib/src/DbClientLockFree.cc index 9f9a44ff..d398e395 100644 --- a/orm_lib/src/DbClientLockFree.cc +++ b/orm_lib/src/DbClientLockFree.cc @@ -45,14 +45,7 @@ DbClientLockFree::DbClientLockFree(const std::string &connInfo, trantor::EventLo { _type = type; LOG_TRACE << "type=" << (int)type; - if (type == ClientType::PostgreSQL) - { - _loop->runInLoop([this]() { - for (size_t i = 0; i < _connectionNum; i++) - _connectionHolders.push_back(newConnection()); - }); - } - else if (type == ClientType::Mysql) + if (type == ClientType::PostgreSQL || type == ClientType::Mysql) { _loop->runInLoop([this]() { for (size_t i = 0; i < _connectionNum; i++) @@ -61,7 +54,7 @@ DbClientLockFree::DbClientLockFree(const std::string &connInfo, trantor::EventLo } else { - LOG_ERROR << "No supported database type!"; + LOG_ERROR << "No supported database type:" << (int)type; } }