The Wayback Machine - https://web.archive.org/web/20220920070128/https://github.com/ithewei/libhv/issues/246
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http长连接时,异步模式的疑似BUG报告 #246

Closed
xwb1966 opened this issue Aug 28, 2022 · 1 comment
Closed

http长连接时,异步模式的疑似BUG报告 #246

xwb1966 opened this issue Aug 28, 2022 · 1 comment

Comments

@xwb1966
Copy link

xwb1966 commented Aug 28, 2022

http长连接时(收发包文不断线,继续下次收发),如果采用异步模式,在压力测试时会随机卡住。以下是测试代码:
HttpService router;
hv::HttpServer hs;

std::mutex locker;
std::list queue;

int main()
{
std::thread t([] {
while (true) { //业务线程
locker.lock();
if (queue.size() == 0) {
locker.unlock();
Sleep(10);
continue;
}

        auto it = queue.begin();
        const HttpContextPtr ctx = *it;
        queue.pop_front();
        locker.unlock();
        int rc = ctx->send("hello");
    }
});

router.POST("/*", [](const HttpContextPtr &ctx) {
    locker.lock();
    queue.push_back(ctx);   //收包后压队列,由业务线程处理
    locker.unlock();
    return 0;
});

hs.registerHttpService(&router);
hs.port = 9002;
http_server_run(&hs, 1);
while (getchar() != 'q');
return 0;

}
在windows下,使用压力测试工具jmeter压测时,只需要10多次来回就大概率会卡住。

通过跟踪,发现在HttpResponseWriter.h中的 End()函数第一句就返回了:if (state == SEND_END) return 0。就是说此时state的值为SEND_END,所以没有回送应答包,导致压测工具等在那里。

正常情况下,state应该等于SEND_BEGIN,它在HttpHandler.cpp中的Init()中,writer.reset()时被置为SEND_BEGIN,但实际上它的值有一定概率会变成SEND_END,导致卡死。

经分析代码,发现在异步模式下,收到包文的HttpHandler处理与回送应答的End()处理不在同一个线程中执行。而state被置为SEND_END是在发送应答包之后置的(End()的最后)。多线程时,End()发送完回答还没有置SEND_END时,压测软件已经收到应答,并发送了下一包;此时被HttpHandler线程收到,并初始化置为SEND_BEGIN,之后才切换到End()的线程,此时又置为SEND_END。导致问题的产生。

修改办法(供参考):
a) End()中将第一行封掉,即不判断state的状态(不明白为什么要判断,某些场景下要连续回送几个包怎么办?);
或者
b) End()中将 state = SEND_END这一句提前到ret = write(msg)之前。

实测上述两种方式都可以解决问题。

@ithewei
Copy link
Owner

ithewei commented Aug 28, 2022

感谢反馈,我已经看懂了这个多线程安全隐患。
a、End里判断状态是为了避免用户多次调用End(),分多次发是可以多次调用WriteBody的
b、在write前设置状态是合理的,我将按照此种方法进行修复

ithewei added a commit that referenced this issue Aug 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants