diff --git a/examples/simple_example/main.cc b/examples/simple_example/main.cc index 958abc60..71ec7f36 100644 --- a/examples/simple_example/main.cc +++ b/examples/simple_example/main.cc @@ -278,6 +278,8 @@ int main() } std::cout << std::get<2>(info) << std::endl; } - + auto resp = HttpResponse::newFileResponse("index.html"); + resp->setExpiredTime(0); + app().setCustom404Page(resp); app().run(); } diff --git a/lib/inc/drogon/CacheMap.h b/lib/inc/drogon/CacheMap.h index 4efda2a6..5a3f4ad7 100644 --- a/lib/inc/drogon/CacheMap.h +++ b/lib/inc/drogon/CacheMap.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #define WHEELS_NUM 4 @@ -127,17 +128,17 @@ class CacheMap }; ~CacheMap() { - _loop->invalidateTimer(_timerId); { std::lock_guard guard(_mtx); _map.clear(); } - - for (int i = _wheels.size() - 1; i >= 0; i--) { - _wheels[i].clear(); + std::lock_guard lock(_bucketMutex); + for (int i = _wheels.size() - 1; i >= 0; i--) + { + _wheels[i].clear(); + } } - LOG_TRACE << "CacheMap destruct!"; } typedef struct MapValue @@ -293,6 +294,15 @@ class CacheMap std::lock_guard lock(_mtx); _map.erase(key); } + /** + * @brief Get the event loop object + * + * @return trantor::EventLoop* + */ + trantor::EventLoop *getLoop() + { + return _loop; + } private: std::unordered_map _map; diff --git a/lib/inc/drogon/IOThreadStorage.h b/lib/inc/drogon/IOThreadStorage.h index 95368d2e..b08516aa 100644 --- a/lib/inc/drogon/IOThreadStorage.h +++ b/lib/inc/drogon/IOThreadStorage.h @@ -14,13 +14,13 @@ #pragma once +#include +#include #include #include #include #include -#include - namespace drogon { /** @@ -56,22 +56,17 @@ namespace drogon * }; * @endcode */ -template -class IOThreadStorage +template +class IOThreadStorage : public trantor::NonCopyable { - static_assert(std::is_constructible::value, - "Unable to construct storage with given signature"); - public: using InitCallback = std::function; + template IOThreadStorage(Args &&... args) - : IOThreadStorage(std::forward(args)..., [](C &, size_t) {}) - { - } - - IOThreadStorage(Args &&... args, const InitCallback &initCB) { + static_assert(std::is_constructible::value, + "Unable to construct storage with given signature"); size_t numThreads = app().getThreadNum(); assert(numThreads > 0 && numThreads != std::numeric_limits::max()); @@ -81,7 +76,14 @@ class IOThreadStorage for (size_t i = 0; i <= numThreads; ++i) { - _storage.emplace_back(std::forward(args)...); + _storage.emplace_back(std::forward(args)...); + } + } + + void init(const InitCallback &initCB) + { + for (size_t i = 0; i < _storage.size(); ++i) + { initCB(_storage[i], i); } } diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index c5bba86e..ca3938b9 100644 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -410,7 +410,7 @@ void HttpAppFrameworkImpl::run() ioLoops.pop_back(); _httpCtrlsRouterPtr->init(ioLoops); _httpSimpleCtrlsRouterPtr->init(ioLoops); - _staticFileRouterPtr->init(); + _staticFileRouterPtr->init(ioLoops); _websockCtrlsRouterPtr->init(); if (_useSession) @@ -764,4 +764,30 @@ void HttpAppFrameworkImpl::quit() { getLoop()->queueInLoop([this]() { getLoop()->quit(); }); } +} + +const HttpResponsePtr &HttpAppFrameworkImpl::getCustom404Page() +{ + if (!_custom404) + { + return _custom404; + } + auto loop = trantor::EventLoop::getEventLoopOfCurrentThread(); + if (loop && loop->index() < app().getThreadNum()) + { + // If the current thread is an IO thread + static IOThreadStorage thread404Pages; + static std::once_flag once; + std::call_once(once, [this] { + thread404Pages.init([this](HttpResponsePtr &resp, size_t index) { + resp = std::make_shared( + *static_cast(_custom404.get())); + }); + }); + return thread404Pages.getThreadData(); + } + else + { + return _custom404; + } } \ No newline at end of file diff --git a/lib/src/HttpAppFrameworkImpl.h b/lib/src/HttpAppFrameworkImpl.h index d434a1b7..44f59874 100644 --- a/lib/src/HttpAppFrameworkImpl.h +++ b/lib/src/HttpAppFrameworkImpl.h @@ -81,10 +81,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework return *this; } - const HttpResponsePtr &getCustom404Page() - { - return _custom404; - } + const HttpResponsePtr &getCustom404Page(); virtual void forward( const HttpRequestPtr &req, diff --git a/lib/src/HttpControllersRouter.cc b/lib/src/HttpControllersRouter.cc index bf792d5b..b3715a69 100644 --- a/lib/src/HttpControllersRouter.cc +++ b/lib/src/HttpControllersRouter.cc @@ -398,7 +398,7 @@ void HttpControllersRouter::doControllerHandler( paraList, req, [=, callback = std::move(callback)](const HttpResponsePtr &resp) { - if (resp->expiredTime() >= 0) + if (resp->expiredTime() >= 0 && resp->statusCode() != k404NotFound) { // cache the response; static_cast(resp.get())->makeHeaderString(); diff --git a/lib/src/HttpResponseImpl.cc b/lib/src/HttpResponseImpl.cc index 1b65e71b..a76699c6 100644 --- a/lib/src/HttpResponseImpl.cc +++ b/lib/src/HttpResponseImpl.cc @@ -16,6 +16,7 @@ #include "HttpAppFrameworkImpl.h" #include "HttpUtils.h" #include +#include #include #include #include @@ -82,27 +83,50 @@ void HttpResponseImpl::generateBodyFromJson() HttpResponsePtr HttpResponse::newNotFoundResponse() { + auto loop = trantor::EventLoop::getEventLoopOfCurrentThread(); auto &resp = HttpAppFrameworkImpl::instance().getCustom404Page(); if (resp) { - return resp; + if (loop && loop->index() < app().getThreadNum()) + { + return resp; + } + else + { + return HttpResponsePtr{new HttpResponseImpl( + *static_cast(resp.get()))}; + } } else { - static std::once_flag once; - static HttpResponsePtr notFoundResp; - std::call_once(once, []() { + if (loop && loop->index() < app().getThreadNum()) + { + // If the current thread is an IO thread + static std::once_flag threadOnce; + static IOThreadStorage thread404Pages; + std::call_once(threadOnce, [] { + thread404Pages.init([](drogon::HttpResponsePtr &resp, + size_t index) { + HttpViewData data; + data.insert("version", getVersion()); + resp = HttpResponse::newHttpViewResponse("drogon::NotFound", + data); + resp->setStatusCode(k404NotFound); + resp->setExpiredTime(0); + }); + }); + LOG_TRACE << "Use cached 404 response"; + return thread404Pages.getThreadData(); + } + else + { HttpViewData data; data.insert("version", getVersion()); - notFoundResp = + auto notFoundResp = HttpResponse::newHttpViewResponse("drogon::NotFound", data); notFoundResp->setStatusCode(k404NotFound); - notFoundResp->setExpiredTime(0); - auto str = static_cast(notFoundResp.get()) - ->renderToString(); - LOG_TRACE << *str; - }); - return notFoundResp; + return notFoundResp; + } } } HttpResponsePtr HttpResponse::newRedirectionResponse( @@ -584,6 +608,8 @@ void HttpResponseImpl::clear() _leftBodyLength = 0; _currentChunkLength = 0; _jsonPtr.reset(); + _expriedTime = -1; + _datePos = std::string::npos; } void HttpResponseImpl::parseJson() const diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index f5396c50..9cec17eb 100644 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -240,6 +240,7 @@ class HttpResponseImpl : public HttpResponse virtual void setExpiredTime(ssize_t expiredTime) override { _expriedTime = expiredTime; + _datePos = std::string::npos; } virtual ssize_t expiredTime() const override diff --git a/lib/src/HttpSimpleControllersRouter.cc b/lib/src/HttpSimpleControllersRouter.cc index ae31fe7c..9b9a8eac 100644 --- a/lib/src/HttpSimpleControllersRouter.cc +++ b/lib/src/HttpSimpleControllersRouter.cc @@ -221,7 +221,8 @@ void HttpSimpleControllersRouter::doControllerHandler( [this, req, callback = std::move(callback), &ctrlBinderPtr]( const HttpResponsePtr &resp) { auto newResp = resp; - if (resp->expiredTime() >= 0) + if (resp->expiredTime() >= 0 && + resp->statusCode() != k404NotFound) { // cache the response; static_cast(resp.get()) diff --git a/lib/src/ListenerManager.cc b/lib/src/ListenerManager.cc index de0d4c18..fd47e8fa 100644 --- a/lib/src/ListenerManager.cc +++ b/lib/src/ListenerManager.cc @@ -186,6 +186,8 @@ std::vector ListenerManager::createListeners( void ListenerManager::startListening() { + if (_listeners.size() == 0) + return; for (auto &loopThread : _listeningloopThreads) { loopThread->run(); diff --git a/lib/src/StaticFileRouter.cc b/lib/src/StaticFileRouter.cc index 34c3d275..3941ef91 100644 --- a/lib/src/StaticFileRouter.cc +++ b/lib/src/StaticFileRouter.cc @@ -26,15 +26,21 @@ using namespace drogon; -void StaticFileRouter::init() +void StaticFileRouter::init(const std::vector &ioloops) { - _responseCachingMap = - std::unique_ptr>( - new CacheMap( - HttpAppFrameworkImpl::instance().getLoop(), - 1.0, - 4, - 50)); // Max timeout up to about 70 days; + // Max timeout up to about 70 days; + _staticFilesCacheMap = decltype(_staticFilesCacheMap)( + new IOThreadStorage>>); + _staticFilesCacheMap->init( + [&ioloops](std::unique_ptr> &mapPtr, + size_t i) { + assert(i == ioloops[i]->index()); + mapPtr = std::unique_ptr>( + new CacheMap(ioloops[i], 1.0, 4, 50)); + }); + _staticFilesCache = decltype(_staticFilesCache)( + new IOThreadStorage< + std::unordered_map>{}); } void StaticFileRouter::route( @@ -62,16 +68,11 @@ void StaticFileRouter::route( } // find cached response HttpResponsePtr cachedResp; + auto &cacheMap = _staticFilesCache->getThreadData(); + auto iter = cacheMap.find(filePath); + if (iter != cacheMap.end()) { - std::lock_guard guard(_staticFilesCacheMutex); - if (_staticFilesCache.find(filePath) != _staticFilesCache.end()) - { - cachedResp = _staticFilesCache[filePath].lock(); - if (!cachedResp) - { - _staticFilesCache.erase(filePath); - } - } + cachedResp = iter->second; } // check last modified time,rfc2616-14.25 @@ -128,6 +129,7 @@ void StaticFileRouter::route( if (cachedResp) { + LOG_TRACE << "Using file cache"; HttpAppFrameworkImpl::instance().callCallback(req, cachedResp, callback); @@ -160,18 +162,18 @@ void StaticFileRouter::route( // cache the response for 5 seconds by default if (_staticFilesCacheTime >= 0) { + LOG_TRACE << "Save in cache for " << _staticFilesCacheTime + << " seconds"; resp->setExpiredTime(_staticFilesCacheTime); - _responseCachingMap->insert( - filePath, resp, resp->expiredTime(), [=]() { - std::lock_guard guard( - _staticFilesCacheMutex); - _staticFilesCache.erase(filePath); + _staticFilesCache->getThreadData()[filePath] = resp; + _staticFilesCacheMap->getThreadData()->insert( + filePath, 0, _staticFilesCacheTime, [this, filePath]() { + LOG_TRACE << "Erase cache"; + assert(_staticFilesCache->getThreadData().find( + filePath) != + _staticFilesCache->getThreadData().end()); + _staticFilesCache->getThreadData().erase(filePath); }); - { - std::lock_guard guard( - _staticFilesCacheMutex); - _staticFilesCache[filePath] = resp; - } } HttpAppFrameworkImpl::instance().callCallback(req, resp, diff --git a/lib/src/StaticFileRouter.h b/lib/src/StaticFileRouter.h index 892f966d..3dbc4459 100644 --- a/lib/src/StaticFileRouter.h +++ b/lib/src/StaticFileRouter.h @@ -16,6 +16,7 @@ #include "impl_forwards.h" #include +#include #include #include #include @@ -41,7 +42,7 @@ class StaticFileRouter { _gzipStaticFlag = useGzipStatic; } - void init(); + void init(const std::vector &ioloops); private: std::set _fileTypeSet = {"html", @@ -64,14 +65,14 @@ class StaticFileRouter "ico", "icns"}; - std::unique_ptr> - _responseCachingMap; - int _staticFilesCacheTime = 5; bool _enableLastModify = true; bool _gzipStaticFlag = true; - std::unordered_map> + std::unique_ptr< + IOThreadStorage>>> + _staticFilesCacheMap; + std::unique_ptr< + IOThreadStorage>> _staticFilesCache; - std::mutex _staticFilesCacheMutex; }; } // namespace drogon diff --git a/orm_lib/src/DbClientManager.cc b/orm_lib/src/DbClientManager.cc index ccd0b631..5599f5ad 100644 --- a/orm_lib/src/DbClientManager.cc +++ b/orm_lib/src/DbClientManager.cc @@ -39,21 +39,20 @@ void DbClientManager::createDbClients( if (dbInfo._dbType == drogon::orm::ClientType::PostgreSQL || dbInfo._dbType == drogon::orm::ClientType::Mysql) { - _dbFastClientsMap.insert( - {dbInfo._name, - IOThreadStorage([&](orm::DbClientPtr &c, - size_t idx) { - assert(idx == ioloops[idx]->index()); - LOG_TRACE - << "create fast database client for the thread " - << idx; - c = std::shared_ptr( - new drogon::orm::DbClientLockFree( - dbInfo._connectionInfo, - ioloops[idx], - dbInfo._dbType, - dbInfo._connectionNumber)); - })}); + _dbFastClientsMap[dbInfo._name] = + IOThreadStorage(); + _dbFastClientsMap[dbInfo._name].init([&](orm::DbClientPtr &c, + size_t idx) { + assert(idx == ioloops[idx]->index()); + LOG_TRACE << "create fast database client for the thread " + << idx; + c = std::shared_ptr( + new drogon::orm::DbClientLockFree( + dbInfo._connectionInfo, + ioloops[idx], + dbInfo._dbType, + dbInfo._connectionNumber)); + }); } } else