drogonframework / drogon

Drogon: A C++14/17/20 based HTTP web application framework running on Linux/macOS/Unix/Windows
MIT License
11.65k stars 1.12k forks source link

FATAL It is forbidden to run loop on threads other than event-loop thread - EventLoop.cc:266 #1515

Open Arniiiii opened 1 year ago

Arniiiii commented 1 year ago

Describe the bug at runtime: FATAL It is forbidden to run loop on threads other than event-loop thread - EventLoop.cc:266

To Reproduce Steps to reproduce the behavior:

  1. Try some links
  2. Get this at runtime
  3. Try another link
  4. Everything is fine example code:
    
    #include <drogon/drogon.h>
    #include <trantor/net/EventLoopThread.h>

class DrogonWrapper { public: DrogonWrapper(trantor::EventLoopThread & loop_a): loop__(loop_a) {} auto getBody(const std::string & origin, const std::string & path) { auto client = drogon::HttpClient::newHttpClient(origin, loop.getLoop()); auto request = drogon::HttpRequest::newHttpRequest(); request->setMethod(drogon::Get); request->setPath(path); request->setPathEncode(false); return (client->sendRequest(request)); } private: trantor::EventLoopThread & loop; };

int main() { std::string origin = "https://api3.binance.com"; std::string path = "/api/v3/exchangeInfo"; auto loop = trantor::EventLoopThread(); loop.run(); DrogonWrapper wrapper(loop); auto pair_result = wrapper.getBody(origin,path); int result; if ("" == pair_result.second->getBody()) { result = 1; } else { result = 0; } return result; }


if path is "/api/v3/time" or "/api/v3/ping" , this error appears. When site is "https://www.apple.com/airtag/" this error appears again.
This error does not appear for "/api/v3/exchangeInfo" . 

**Expected behavior**
No error I guess.

**Desktop (please complete the following information):**
 - OS: Manjaro KDE
 - Kernel: Linux 5.15.94-1 and 6.1.12-1
 - Drogon version: 1.8.2
 - compiler: GCC 12.2.1 20230201 and Clang 15.0.7

**Additional info**
For documentation of binance api : https://binance-docs.github.io/apidocs/spot/en/#market-data-endpoints
Arniiiii commented 1 year ago

more examples when it's happening: ( just change path to /api/v3/time or /api/v3/ping and this error appears )

#include <drogon/drogon.h>
#include <trantor/net/EventLoopThread.h>

int main() {
    std::string origin = "https://api3.binance.com";
    std::string path = "/api/v3/exchangeInfo";
    auto loop = trantor::EventLoopThread();
    loop.run();
    auto client = drogon::HttpClient::newHttpClient(origin, loop.getLoop());
    auto request = drogon::HttpRequest::newHttpRequest();
    request->setMethod(drogon::Get);
    request->setPath(path);
    request->setPathEncode(false);
    auto pair_result = client->sendRequest(request);
    int result;
    if ("" == pair_result.second->getBody())
    {
        result = 1;
    }
    else {
        result = 0;
    }
    return result;
}

or like this:

#include <drogon/drogon.h>
#include <trantor/net/EventLoopThread.h>

class DrogonWrapper {
public:
    DrogonWrapper(trantor::EventLoop * loop_a): loop__(loop_a) {}
    auto getBody(const std::string & origin, const std::string & path) {
        auto client = drogon::HttpClient::newHttpClient(origin, loop__);
        auto request = drogon::HttpRequest::newHttpRequest();
        request->setMethod(drogon::Get);
        request->setPath(path);
        request->setPathEncode(false);
        return (client->sendRequest(request));
    }
private:
trantor::EventLoop * loop__;
};

int main() {
    std::string origin = "https://api3.binance.com";
    std::string path = "/api/v3/exchangeInfo";
    auto loop = trantor::EventLoopThread();
    loop.run();
    DrogonWrapper wrapper(loop.getLoop());
    auto pair_result = wrapper.getBody(origin,path);
    int result;
    if ("" == pair_result.second->getBody())
    {
        result = 1;
    }
    else {
        result = 0;
    }
    return result;
}
Arniiiii commented 1 year ago

I would check it with drogon 1.8.3 if it was in vckpg, but there's no such.

nikodatinh commented 1 year ago

I also got the same error when I tried to run the benchmark with the code below:

int main() {
    try {
        //Set HTTP listener address and port
        drogon::app().addListener("0.0.0.0",3000);
        drogon::app().setLogLevel(trantor::Logger::kDebug); // Set the log level (optional)
        drogon::app().registerHandler("/benchmark",[](const drogon::HttpRequestPtr &req,std::function<void (const drogon::HttpResponsePtr &)> &&callback){

                auto db = drogon::app().getDbClient("test");
                db -> execSqlAsync("SELECT COUNT(id) FROM users", [callback](const drogon::orm::Result &result){
                    // call the callback function with the result
                    auto resp = drogon::HttpResponse::newHttpResponse();
                    resp->setStatusCode(drogon::HttpStatusCode::k200OK);
                    resp->setContentTypeCode(drogon::ContentType::CT_APPLICATION_JSON);
                    resp->setBody("{\"status\":\"OK\",\"message\":\"Benchmarking\"}");
                    callback(resp);

                },[callback](const drogon::orm::DrogonDbException &e){
                    LOG_ERROR << "DB error: " << e.base().what();
                    auto resp = drogon::HttpResponse::newHttpResponse();
                    resp->setStatusCode(drogon::HttpStatusCode::k500InternalServerError);
                    resp->setContentTypeCode(drogon::ContentType::CT_APPLICATION_JSON);
                    resp->setBody("{\"status\":\"ERROR\",\"message\":\"Internal Server Error\"}");
                    callback(resp);
                });

        });
        drogon::app().loadConfigFile("config_benchmark.json");
        drogon::app().setThreadNum(16);
        drogon::app().setIdleConnectionTimeout(10);
        drogon::app().run();
    }
    catch (std::exception &e) {
        LOG_INFO << "exception : " << e.what();
        return 1; // or handle the error appropriately
    }
}

my file configuration as below:

{
    "listeners": [
        {
            "address": "0.0.0.0",
            "port": 3000,
            "https": false
        }
    ],
    "db_clients": [
        {
            "name":"test",
            "rdbms": "mysql",
            "host": "localhost",
            "port": 3306,
            "dbname": "test",
            "user": "root",
            "passwd": "root",
            "is_fast": false,
            "number_of_connections": 100,
            "timeout": 120,
            "auto_batch": true
        }       
    ]

}

Is there anything wrong with my code? I use JMeter for benchmarking with the following configuration: image