add onSessionStart() and onSessionDestroy() events (#1412)

Co-authored-by: an-tao <antao2002@gmail.com>
This commit is contained in:
Francesco Emanuele D'Agostino 2022-11-13 15:40:24 +01:00 committed by GitHub
parent ef93c91ec7
commit 1b11bfb668
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 153 additions and 35 deletions

View File

@ -86,12 +86,16 @@ class CacheMap
CacheMap(trantor::EventLoop *loop, CacheMap(trantor::EventLoop *loop,
float tickInterval = TICK_INTERVAL, float tickInterval = TICK_INTERVAL,
size_t wheelsNum = WHEELS_NUM, size_t wheelsNum = WHEELS_NUM,
size_t bucketsNumPerWheel = BUCKET_NUM_PER_WHEEL) size_t bucketsNumPerWheel = BUCKET_NUM_PER_WHEEL,
std::function<void(const T1 &)> fnOnInsert = nullptr,
std::function<void(const T1 &)> fnOnErase = nullptr)
: loop_(loop), : loop_(loop),
tickInterval_(tickInterval), tickInterval_(tickInterval),
wheelsNumber_(wheelsNum), wheelsNumber_(wheelsNum),
bucketsNumPerWheel_(bucketsNumPerWheel), bucketsNumPerWheel_(bucketsNumPerWheel),
ctrlBlockPtr_(std::make_shared<ControlBlock>()) ctrlBlockPtr_(std::make_shared<ControlBlock>()),
fnOnInsert_(fnOnInsert),
fnOnErase_(fnOnErase)
{ {
wheels_.resize(wheelsNumber_); wheels_.resize(wheelsNumber_);
for (size_t i = 0; i < wheelsNumber_; ++i) for (size_t i = 0; i < wheelsNumber_; ++i)
@ -215,6 +219,8 @@ class CacheMap
std::lock_guard<std::mutex> lock(mtx_); std::lock_guard<std::mutex> lock(mtx_);
map_.insert(std::make_pair(key, std::move(v))); map_.insert(std::make_pair(key, std::move(v)));
} }
if (fnOnInsert_)
fnOnInsert_(key);
} }
/** /**
* @brief Insert a key-value pair into the cache. * @brief Insert a key-value pair into the cache.
@ -244,6 +250,8 @@ class CacheMap
std::lock_guard<std::mutex> lock(mtx_); std::lock_guard<std::mutex> lock(mtx_);
map_.insert(std::make_pair(key, std::move(v))); map_.insert(std::make_pair(key, std::move(v)));
} }
if (fnOnInsert_)
fnOnInsert_(key);
} }
/** /**
@ -284,9 +292,11 @@ class CacheMap
* the key doesn't exist, a new one is created and passed to the handler and * the key doesn't exist, a new one is created and passed to the handler and
* stored in the cache with the timeout parameter. The changing of the data * stored in the cache with the timeout parameter. The changing of the data
* is protected by the mutex of the cache. * is protected by the mutex of the cache.
*
*/ */
template <typename Callable> template <typename Callable>
void modify(const T1 &key, Callable &&handler, size_t timeout = 0) void modify(const T1 &key, Callable &&handler, size_t timeout = 0)
{
{ {
std::lock_guard<std::mutex> lock(mtx_); std::lock_guard<std::mutex> lock(mtx_);
auto iter = map_.find(key); auto iter = map_.find(key);
@ -298,6 +308,7 @@ class CacheMap
eraseAfter(timeout, key); eraseAfter(timeout, key);
return; return;
} }
MapValue v{T2(), timeout}; MapValue v{T2(), timeout};
handler(v.value_); handler(v.value_);
map_.insert(std::make_pair(key, std::move(v))); map_.insert(std::make_pair(key, std::move(v)));
@ -306,6 +317,10 @@ class CacheMap
eraseAfter(timeout, key); eraseAfter(timeout, key);
} }
} }
if (fnOnInsert_)
fnOnInsert_(key);
}
/// Check if the value of the keyword exists /// Check if the value of the keyword exists
bool find(const T1 &key) bool find(const T1 &key)
{ {
@ -358,9 +373,13 @@ class CacheMap
void erase(const T1 &key) void erase(const T1 &key)
{ {
// in this case,we don't evoke the timeout callback; // in this case,we don't evoke the timeout callback;
{
std::lock_guard<std::mutex> lock(mtx_); std::lock_guard<std::mutex> lock(mtx_);
map_.erase(key); map_.erase(key);
} }
if (fnOnErase_)
fnOnErase_(key);
}
/** /**
* @brief Get the event loop object * @brief Get the event loop object
* *
@ -425,6 +444,8 @@ class CacheMap
size_t wheelsNumber_; size_t wheelsNumber_;
size_t bucketsNumPerWheel_; size_t bucketsNumPerWheel_;
std::shared_ptr<ControlBlock> ctrlBlockPtr_; std::shared_ptr<ControlBlock> ctrlBlockPtr_;
std::function<void(const T1 &)> fnOnInsert_;
std::function<void(const T1 &)> fnOnErase_;
bool noWheels_{false}; bool noWheels_{false};
@ -486,21 +507,28 @@ class CacheMap
else else
{ {
std::function<void()> cb = [this, key]() { std::function<void()> cb = [this, key]() {
std::lock_guard<std::mutex> lock(mtx_); bool erased{false};
if (map_.find(key) != map_.end()) std::function<void()> timeoutCallback;
{ {
auto &value = map_[key]; std::lock_guard<std::mutex> lock(mtx_);
auto iter = map_.find(key);
if (iter != map_.end())
{
auto &value = iter->second;
auto entryPtr = value.weakEntryPtr_.lock(); auto entryPtr = value.weakEntryPtr_.lock();
// entryPtr is used to avoid race conditions // entryPtr is used to avoid race conditions
if (value.timeout_ > 0 && !entryPtr) if (value.timeout_ > 0 && !entryPtr)
{ {
if (value.timeoutCallback_) erased = true;
{ timeoutCallback = std::move(value.timeoutCallback_);
value.timeoutCallback_();
}
map_.erase(key); map_.erase(key);
} }
} }
}
if (erased && fnOnErase_)
fnOnErase_(key);
if (erased && timeoutCallback)
timeoutCallback();
}; };
entryPtr = std::make_shared<CallbackEntry>(std::move(cb)); entryPtr = std::make_shared<CallbackEntry>(std::move(cb));
map_[key].weakEntryPtr_ = entryPtr; map_[key].weakEntryPtr_ = entryPtr;

View File

@ -745,6 +745,20 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
return enableSession((size_t)timeout.count(), sameSite); return enableSession((size_t)timeout.count(), sameSite);
} }
/// Register an advice called when starting a new session.
/**
* @param advice is called with the session id.
*/
virtual HttpAppFramework &registerSessionStartAdvice(
const AdviceStartSessionCallback &advice) = 0;
/// Register an advice called when destroying a session.
/**
* @param advice is called with the session id.
*/
virtual HttpAppFramework &registerSessionDestroyAdvice(
const AdviceDestroySessionCallback &advice) = 0;
/// Disable sessions supporting. /// Disable sessions supporting.
/** /**
* @note * @note

View File

@ -26,6 +26,8 @@ class HttpRequest;
using HttpRequestPtr = std::shared_ptr<HttpRequest>; using HttpRequestPtr = std::shared_ptr<HttpRequest>;
using AdviceCallback = std::function<void(const HttpResponsePtr &)>; using AdviceCallback = std::function<void(const HttpResponsePtr &)>;
using AdviceChainCallback = std::function<void()>; using AdviceChainCallback = std::function<void()>;
using AdviceStartSessionCallback = std::function<void(const std::string &)>;
using AdviceDestroySessionCallback = std::function<void(const std::string &)>;
using FilterCallback = std::function<void(const HttpResponsePtr &)>; using FilterCallback = std::function<void(const HttpResponsePtr &)>;
using FilterChainCallback = std::function<void()>; using FilterChainCallback = std::function<void()>;
using HttpReqCallback = std::function<void(ReqResult, const HttpResponsePtr &)>; using HttpReqCallback = std::function<void(ReqResult, const HttpResponsePtr &)>;

View File

@ -571,7 +571,10 @@ void HttpAppFrameworkImpl::run()
if (useSession_) if (useSession_)
{ {
sessionManagerPtr_ = sessionManagerPtr_ =
std::make_unique<SessionManager>(getLoop(), sessionTimeout_); std::make_unique<SessionManager>(getLoop(),
sessionTimeout_,
sessionStartAdvices_,
sessionDestroyAdvices_);
} }
// now start running!! // now start running!!
running_ = true; running_ = true;

View File

@ -221,6 +221,21 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
useSession_ = false; useSession_ = false;
return *this; return *this;
} }
HttpAppFramework &registerSessionStartAdvice(
const AdviceStartSessionCallback &advice) override
{
sessionStartAdvices_.emplace_back(advice);
return *this;
}
HttpAppFramework &registerSessionDestroyAdvice(
const AdviceDestroySessionCallback &advice) override
{
sessionDestroyAdvices_.emplace_back(advice);
return *this;
}
const std::string &getDocumentRoot() const override const std::string &getDocumentRoot() const override
{ {
return rootPath_; return rootPath_;
@ -657,6 +672,8 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
std::function<void()> termSignalHandler_{[]() { app().quit(); }}; std::function<void()> termSignalHandler_{[]() { app().quit(); }};
std::function<void()> intSignalHandler_{[]() { app().quit(); }}; std::function<void()> intSignalHandler_{[]() { app().quit(); }};
std::unique_ptr<SessionManager> sessionManagerPtr_; std::unique_ptr<SessionManager> sessionManagerPtr_;
std::vector<AdviceStartSessionCallback> sessionStartAdvices_;
std::vector<AdviceDestroySessionCallback> sessionDestroyAdvices_;
std::shared_ptr<trantor::AsyncFileLogger> asyncFileLoggerPtr_; std::shared_ptr<trantor::AsyncFileLogger> asyncFileLoggerPtr_;
Json::Value jsonConfig_; Json::Value jsonConfig_;
HttpResponsePtr custom404_; HttpResponsePtr custom404_;

View File

@ -17,8 +17,15 @@
using namespace drogon; using namespace drogon;
SessionManager::SessionManager(trantor::EventLoop *loop, size_t timeout) SessionManager::SessionManager(
: loop_(loop), timeout_(timeout) trantor::EventLoop* loop,
size_t timeout,
const std::vector<AdviceStartSessionCallback>& startAdvices,
const std::vector<AdviceDestroySessionCallback>& destroyAdvices)
: loop_(loop),
timeout_(timeout),
sessionStartAdvices_(startAdvices),
sessionDestroyAdvices_(destroyAdvices)
{ {
if (timeout_ > 0) if (timeout_ > 0)
{ {
@ -38,25 +45,57 @@ SessionManager::SessionManager(trantor::EventLoop *loop, size_t timeout)
tmpTimeout = tmpTimeout / 100; tmpTimeout = tmpTimeout / 100;
} }
} }
sessionMapPtr_ = std::unique_ptr<CacheMap<std::string, SessionPtr>>( sessionMapPtr_ = std::unique_ptr<CacheMap<std::string, SessionPtr>>(
new CacheMap<std::string, SessionPtr>( new CacheMap<std::string, SessionPtr>(
loop_, 1.0, wheelNum, bucketNum)); loop_,
1.0,
wheelNum,
bucketNum,
[this](const std::string& key) {
for (auto& advice : sessionStartAdvices_)
{
advice(key);
}
},
[this](const std::string& key) {
for (auto& advice : sessionDestroyAdvices_)
{
advice(key);
}
}));
} }
else if (timeout_ == 0) else if (timeout_ == 0)
{ {
sessionMapPtr_ = std::unique_ptr<CacheMap<std::string, SessionPtr>>( sessionMapPtr_ = std::unique_ptr<CacheMap<std::string, SessionPtr>>(
new CacheMap<std::string, SessionPtr>(loop_, 0, 0, 0)); new CacheMap<std::string, SessionPtr>(
loop_,
0,
0,
0,
[this](const std::string& key) {
for (auto& advice : sessionStartAdvices_)
{
advice(key);
}
},
[this](const std::string& key) {
for (auto& advice : sessionDestroyAdvices_)
{
advice(key);
}
}));
} }
} }
SessionPtr SessionManager::getSession(const std::string &sessionID, SessionPtr SessionManager::getSession(const std::string& sessionID,
bool needToSet) bool needToSet)
{ {
assert(!sessionID.empty()); assert(!sessionID.empty());
SessionPtr sessionPtr; SessionPtr sessionPtr;
sessionMapPtr_->modify( sessionMapPtr_->modify(
sessionID, sessionID,
[&sessionPtr, &sessionID, needToSet](SessionPtr &sessionInCache) { [&sessionPtr, &sessionID, needToSet](SessionPtr& sessionInCache) {
if (sessionInCache) if (sessionInCache)
{ {
sessionPtr = sessionInCache; sessionPtr = sessionInCache;
@ -69,10 +108,11 @@ SessionPtr SessionManager::getSession(const std::string &sessionID,
} }
}, },
timeout_); timeout_);
return sessionPtr; return sessionPtr;
} }
void SessionManager::changeSessionId(const SessionPtr &sessionPtr) void SessionManager::changeSessionId(const SessionPtr& sessionPtr)
{ {
auto oldId = sessionPtr->sessionId(); auto oldId = sessionPtr->sessionId();
auto newId = utils::getUuid(); auto newId = utils::getUuid();

View File

@ -15,19 +15,25 @@
#pragma once #pragma once
#include <drogon/Session.h> #include <drogon/Session.h>
#include <drogon/drogon_callbacks.h>
#include <drogon/CacheMap.h> #include <drogon/CacheMap.h>
#include <trantor/utils/NonCopyable.h> #include <trantor/utils/NonCopyable.h>
#include <trantor/net/EventLoop.h> #include <trantor/net/EventLoop.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <mutex> #include <mutex>
#include <vector>
namespace drogon namespace drogon
{ {
class SessionManager : public trantor::NonCopyable class SessionManager : public trantor::NonCopyable
{ {
public: public:
SessionManager(trantor::EventLoop *loop, size_t timeout); SessionManager(
trantor::EventLoop *loop,
size_t timeout,
const std::vector<AdviceStartSessionCallback> &startAdvices,
const std::vector<AdviceDestroySessionCallback> &destroyAdvices);
~SessionManager() ~SessionManager()
{ {
sessionMapPtr_.reset(); sessionMapPtr_.reset();
@ -39,5 +45,7 @@ class SessionManager : public trantor::NonCopyable
std::unique_ptr<CacheMap<std::string, SessionPtr>> sessionMapPtr_; std::unique_ptr<CacheMap<std::string, SessionPtr>> sessionMapPtr_;
trantor::EventLoop *loop_; trantor::EventLoop *loop_;
size_t timeout_; size_t timeout_;
const std::vector<AdviceStartSessionCallback> &sessionStartAdvices_;
const std::vector<AdviceDestroySessionCallback> &sessionDestroyAdvices_;
}; };
} // namespace drogon } // namespace drogon

View File

@ -319,6 +319,12 @@ int main()
} }
return nullResp; return nullResp;
}); });
app().registerSessionStartAdvice([](const std::string &sessionId) {
LOG_DEBUG << "session start:" << sessionId;
});
app().registerSessionDestroyAdvice([](const std::string &sessionId) {
LOG_DEBUG << "session destroy:" << sessionId;
});
// Output information of all handlers // Output information of all handlers
auto handlerInfo = app().getHandlersInfo(); auto handlerInfo = app().getHandlersInfo();
for (auto &info : handlerInfo) for (auto &info : handlerInfo)