我们一直在使用优秀的cpp-httlib来发出 REST 请求。现在我们想将它用作服务器。文档顶部的内容如下:
重要的
此库使用“阻塞”套接字 I/O。如果您正在寻找具有“非阻塞”套接字 I/O 的库,那么这不是您想要的。
我不确定这在实际意义上意味着什么,因为虽然我了解 IP/UDP/TCP 等的基本原理,但我不熟悉套接字编程。我通过向服务器发出多个请求来测试服务器,例如,如果第一个请求通过休眠 30 秒来实现,那么第二个“hello world”请求仍会立即返回。
调试代码,我看到主线程运行:
while (svr_sock_ != INVALID_SOCKET) {
// socket operations
if (!task_queue->enqueue(
[this, sock]() { process_and_close_socket(sock); })) {
detail::shutdown_socket(sock);
detail::close_socket(sock);
}
}
工作线程运行
for (;;) {
std::function<void()> fn;
{
// Lock / wait for new request
fn = pool_.jobs_.front();
pool_.jobs_.pop_front();
}
fn();
}
即主线程就像 GUI 应用程序中的事件循环,接收每个传入请求,但不是执行请求本身,而是将其放在队列中以供工作线程接收。
以前,我在 Python FastAPI Web 服务器中调用了 C++,这很好,但同时运行 Python 和 C++ 显然会增加开销/复杂性,而且很难理解请求是如何在全局解释器锁的情况下执行的。使用 cpp-httlib 的直接 C++ 方法看起来好多了,但关于“阻塞”套接字 I/O 的评论让我担心。不使用非阻塞套接字 I/O 会让我错过什么?
(该服务器将针对 Windows 和 Linux 进行构建;但产品将是 Linux。)
阻塞 IO 库使用线程执行抢占式多任务处理,而非阻塞 IO 库执行协作式多任务处理,另请参阅协作式多任务处理和抢占式多任务处理之间有什么区别?
使用抢占式多任务(cpp-httplib)如果您想要一次处理 1000 个套接字那么您将需要激活 1000 个线程,并且大部分 CPU 时间将浪费在操作系统调度上而不是执行实际工作,上下文切换可能需要几微秒,在云提供商使用的虚拟化操作系统上甚至可能需要更多时间。
使用协作式多任务处理(asio),如果您有一个 4 核的服务器,您只需要 4 个线程来管理 1000 个套接字,操作系统将不必进行任何过多的调度,并且您的应用程序将处理这种额外的调度,但在用户空间中使用更少的系统调用,最终会更快。(因为操作系统具有复杂的调度程序,而用户空间调度程序很简单,因此速度很快)。
如果你每个任务都在进行大量计算或者同时管理有限数量的套接字并且不受 IO 限制,那么使用抢占式多任务(cpp-httplib)是可以的,但如果你受到 IO 限制(例如 html 或文件服务器),那么您应该使用协作式多任务(asio)。
协作式多任务(asio)可能更难使用,但速度更快,而抢占式多任务(cpp-httplib)可能更慢,但更易于使用。
相比之下,FastApi对路径使用协作式多任务处理
async
,对非异步路径使用抢占式多任务处理。