diff --git a/config.example.json b/config.example.json index 0d4f3e5b..17847be9 100644 --- a/config.example.json +++ b/config.example.json @@ -217,7 +217,9 @@ "client_max_memory_body_size": "64K", //client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K". //One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit. - "client_max_websocket_message_size": "128K" + "client_max_websocket_message_size": "128K", + //reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time. + "reuse_port": false }, //plugins: Define all plugins running in the application "plugins": [{ diff --git a/drogon_ctl/templates/config.csp b/drogon_ctl/templates/config.csp index 16d6cf2e..4ace2a98 100644 --- a/drogon_ctl/templates/config.csp +++ b/drogon_ctl/templates/config.csp @@ -217,7 +217,9 @@ "client_max_memory_body_size": "64K", //client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K". //One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit. - "client_max_websocket_message_size": "128K" + "client_max_websocket_message_size": "128K", + //reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time. + "reuse_port": false }, //plugins: Define all plugins running in the application "plugins": [{ diff --git a/examples/simple_example/TimeFilter.cc b/examples/simple_example/TimeFilter.cc index ebb3abe6..46026d01 100644 --- a/examples/simple_example/TimeFilter.cc +++ b/examples/simple_example/TimeFilter.cc @@ -20,7 +20,10 @@ void TimeFilter::doFilter(const HttpRequestPtr &req, { auto lastDate = req->session()->get(VDate); LOG_TRACE << "last:" << lastDate.toFormattedString(false); - req->session()->insert(VDate, now); + req->session()->modify(VDate, + [now](trantor::Date &vdate) { + vdate = now; + }); LOG_TRACE << "update visitDate"; if (now > lastDate.after(10)) { diff --git a/lib/inc/drogon/HttpAppFramework.h b/lib/inc/drogon/HttpAppFramework.h index ac49b075..d7723ac8 100644 --- a/lib/inc/drogon/HttpAppFramework.h +++ b/lib/inc/drogon/HttpAppFramework.h @@ -1172,6 +1172,21 @@ class HttpAppFramework : public trantor::NonCopyable */ virtual std::vector getListeners() const = 0; + /** + * @brief Enable ReusePort mode or not. If the mode is enabled, one can run + * multiple processes listening to the same port at the same time. If this + * method is not called, the feature is disabled. + * + * @note + * This operation can be performed by an option in the configuration file. + */ + virtual void enableReusePort(bool enable = true) = 0; + + /** + * @brief Return if the ReusePort mode is enabled. + */ + virtual bool reusePort() const = 0; + private: virtual void registerHttpController( const std::string &pathPattern, diff --git a/lib/inc/drogon/Session.h b/lib/inc/drogon/Session.h index 007c2823..f1e29a70 100644 --- a/lib/inc/drogon/Session.h +++ b/lib/inc/drogon/Session.h @@ -1,7 +1,7 @@ /** * * @file Session.h - * An Tao + * @author An Tao * * Copyright 2018, An Tao. All rights reserved. * https://github.com/an-tao/drogon @@ -117,6 +117,7 @@ class Session * @code sessionPtr->insert("user name", userNameString); @endcode + * @note If the key already exists, the element is not inserted. */ void insert(const std::string &key, const any &obj) { @@ -130,6 +131,7 @@ class Session * @code sessionPtr->insert("user name", userNameString); @endcode + * @note If the key already exists, the element is not inserted. */ void insert(const std::string &key, any &&obj) { diff --git a/lib/src/ConfigLoader.cc b/lib/src/ConfigLoader.cc index 3280fbd9..d4d85e27 100644 --- a/lib/src/ConfigLoader.cc +++ b/lib/src/ConfigLoader.cc @@ -452,6 +452,7 @@ static void loadApp(const Json::Value &app) << std::endl; exit(1); } + drogon::app().enableReusePort(app.get("reuse_port", false).asBool()); drogon::app().setHomePage(app.get("home_page", "index.html").asString()); } static void loadDbClients(const Json::Value &dbClients) diff --git a/lib/src/HttpAppFrameworkImpl.h b/lib/src/HttpAppFrameworkImpl.h index 19575015..06da8c23 100644 --- a/lib/src/HttpAppFrameworkImpl.h +++ b/lib/src/HttpAppFrameworkImpl.h @@ -488,6 +488,14 @@ class HttpAppFrameworkImpl : public HttpAppFramework { return usingCustomErrorHandler_; } + virtual void enableReusePort(bool enable = true) override + { + reusePort_ = enable; + } + virtual bool reusePort() const override + { + return reusePort_; + } private: virtual void registerHttpController( @@ -581,6 +589,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework static InitBeforeMainFunction initFirst_; bool enableServerHeader_{true}; bool enableDateHeader_{true}; + bool reusePort_{false}; std::vector> beginningAdvices_; std::vector> diff --git a/lib/src/HttpServer.cc b/lib/src/HttpServer.cc index a4c7cb56..cca34d67 100644 --- a/lib/src/HttpServer.cc +++ b/lib/src/HttpServer.cc @@ -1,7 +1,7 @@ /** * * @file HttpServer.cc - * An Tao + * @author An Tao * * Copyright 2018, An Tao. All rights reserved. * https://github.com/an-tao/drogon @@ -158,7 +158,7 @@ HttpServer::HttpServer( #ifdef __linux__ : server_(loop, listenAddr, name.c_str()), #else - : server_(loop, listenAddr, name.c_str(), true, false), + : server_(loop, listenAddr, name.c_str(), true, app().reusePort()), #endif httpAsyncCallback_(defaultHttpAsyncCallback), newWebsocketCallback_(defaultWebSockAsyncCallback), diff --git a/lib/src/ListenerManager.cc b/lib/src/ListenerManager.cc index 98cf8754..ecd3894d 100644 --- a/lib/src/ListenerManager.cc +++ b/lib/src/ListenerManager.cc @@ -91,7 +91,7 @@ std::vector ListenerManager::createListeners( auto const &ip = listener.ip_; bool isIpv6 = ip.find(':') == std::string::npos ? false : true; std::shared_ptr serverPtr; - if (i == 0) + if (i == 0 && !app().reusePort()) { DrogonFileLocker lock; // Check whether the port is in use.