Indy 服务器 — 线程模型(1 / 3)

· 功能

Indy 服务器使用线程处理客户端连接,每当新客户端连接到服务器时,就会创建一个新线程来处理该连接。因此,如果有 100 个连接,就会有 100 个线程。此外,Indy 使用阻塞套接字,这意味着读写操作不完成函数就不会返回。

这种模型有一些优点,例如代码简单,因为代码是顺序执行的。但缺点是,随着连接数量增加,由于线程上下文切换,性能会越来越差。上下文切换是存储线程状态以便稍后恢复执行的过程,频繁的线程上下文切换会消耗大量 CPU 资源。如果服务器创建了 1000 个连接,即使没有数据交换,您也会看到 CPU 持续运转,这正是线程上下文切换造成的。

Indy 模型的替代方案

除了为每个连接使用一个线程之外,还有 IOCP(适用于 Windows)和 EPOLL(适用于 Linux)等替代方案,它们使用线程池处理连接并采用非阻塞套接字。当并发连接数较高时,这种模型效率更高,扩展性也远优于默认的 Indy 线程模型。

IOCP(Windows)

I/O 完成端口为多处理器系统上处理多个异步 I/O 请求提供了高效的线程模型。当进程创建 I/O 完成端口时,系统会创建一个关联的队列对象,专供线程处理这些请求。通过将 I/O 完成端口与预分配线程池结合使用,处理大量并发异步 I/O 请求的进程可以比在收到请求时临时创建线程更快速、更高效地完成工作。

IOCP 模型使用非阻塞套接字,用 AcceptEx 函数和线程池代替 Select 来处理客户端连接。

要在 sgcWebSockets Indy 服务器上启用 IOCP,请参考以下代码:

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.NotifyEvents := neNOSync;
oServer.IOHandlerOptions.IOHandlerType := iohIOCP;
oServer.Active := True; 

将创建一个新的 WebSocket + HTTP 服务器,由线程池处理连接,使用的线程数量取决于服务器运行所在的 CPU 数量。

EPOLL(Linux)

Epoll 是 Linux 内核提供的一种可扩展 I/O 事件通知机制的系统调用,最初在 Linux 内核 2.5.44 版本中引入。其功能是监视多个文件描述符,以查看其中是否有 I/O 可以执行。它旨在替代较旧的 POSIX select 和 poll 系统调用,在监视的文件描述符数量较多的高要求应用场景中实现更好的性能。

以下表格对比了 100,000 次监视操作的性能:

操作次数 poll select epoll
10 0.61 0.73 0.41
1002.93.00.42
100035350.53
100009909300.66

可以看出,当需要监视的文件描述符超过 10 个左右时,epoll 的速度要快得多。

EPOLL 模型使用非阻塞套接字,用异步 Accept 函数和线程池代替 Select 来处理客户端连接。

要在 sgcWebSockets Indy 服务器上启用 EPOLL,请参考以下代码:

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.NotifyEvents := neNOSync;
oServer.IOHandlerOptions.IOHandlerType := iohEPOLL;
oServer.Active := True;