mormot2 http服务器的实现
mORMot 2 采用了mORMot 1 源代码中的所有 HTTP 服务器类。然后包括一些新的“异步”服务器。
它们都继承自THttpServerGeneric
父类。
HTTP 服务器在几个单元中实现:
- mORMot.net.server.pas提供了/HTTP/1.1服务器,
THttpServerSocket
/1.1服务器over Windows http.sys模块,over Windows http.sys模块;THttpServer
THttpApiServer
THttpApiWebSocketServer
- mORMot .net.ws.server.pas提供
TWebSocketServerRest
服务器,它使用 WebSockets 作为传输方式,但在其之上启用类似 REST 的阻塞请求/应答协议,具有可选的双向通知,使用单线程 -每个连接服务器; - mORMot .net.async.pas提供了新的
THttpAsyncServer
事件驱动的 HTTP 服务器; - mORMot .net.ws.async.pas提供新的
TWebSocketAsyncServerRest
服务器,它使用 WebSockets 作为传输方式,但在其之上启用类似 REST 的阻塞请求/应答协议,具有可选的双向通知,使用事件驱动服务器。
在 Windows 上,http.sys模块为您提供了非常好的稳定性,并使用与 IIS 和 DotNet 使用的相同的以 Windows 为中心的服务器发布方式。如果需要,您甚至可以在多个服务之间共享同一个端口。
我们基于套接字的服务器是跨平台的,可以在 Windows 和 POSIX(Linux、BSD、MacOS)上编译和运行。他们为 HTTP/1.0 的短期请求使用线程池,在 HTTP/1.1 上为每个连接使用一个线程。所以它们应该在像 nginx 这样的反向代理后面使用,它可以使用mORMot通过 HTTP/1.0 传输,但保持高效的 HTTP/1.1 或 HTTP/2.0 与客户端通信。
mORMot .net.async.pas和mORMot .net.ws.async.pas都是mORMot 2的新功能。它们使用事件驱动模型,即使用快速 API(如 Linux 上的 epoll)跟踪打开的连接,并且只有当实际有新数据挂起时才使用线程池。
永远的事件
异步套接字访问和事件循环是最佳服务器可伸缩性的关键。对于THttpServerSocket
每个 HTTP/1.1 或 WebSockets 连接使用一个线程的常规类,我们的异步类(例如THttpAsyncServer
)可以有数千个并发客户端,CPU 和 RAM 资源消耗最少。
在mORMot 2 网络核心中,即在单元mORMot.net.sock.pas中,我们定义了一个抽象的事件驱动类:
TPollSockets
根据操作系统的不同,它将使用底层的低级系统调用来模拟epoll api 。
事件非常抽象,实际上只是每个连接上的基本 R/W 操作,与“标签”相关联,这很可能是与套接字/连接相关联的类指针:
然后在这些基本的套接字驱动事件之上编写了全新的 HTTP/1.0、HTTP/1.1 和 WebSockets 堆栈。它们没有阻塞线程,而是使用内部状态机,它比线程轻得多,甚至比协程/goroutine 更轻。每个连接只是一个类实例,它维护每个客户端/服务器通信的状态,并访问它自己的套接字。
mORMot异步 TCP 服务器默认有一个线程接受连接,一个线程轮询未决事件(调用GetOne
方法),然后一组专用线程使用读/写/关闭事件(通过GetOnePending
方法)。我们使用了尽可能多的非阻塞结构,我们通过重复使用相同的缓冲区来最小化内存分配,例如用于标头或小响应,我们在每种情况下都尽可能选择最好的锁,以便该服务器可以很好地平滑地扩展。而且使用简单,因为处理一个新的协议就像继承和编写一个新的连接类一样简单。
我们的异步服务器类现在看起来稳定且快速(据报道比 nginx 快两倍,比 nodejs 快六倍!)。
但当然,与任何新的复杂代码一样,它们可能是警告。其中一些已经被识别和修复——正如我们论坛中所报告的那样。因此,欢迎反馈,nginx、haproxy 或 caddy 反向代理前端在生产环境中始终是一个好主意。
编写这些服务器所花的时间比预览的要多,有时还很痛苦。因为调试多线程进程并不容易,尤其是在多个操作系统上。操作系统之间存在一些细微差别,可能会导致意外阻塞或性能下降。但我们为与同类最佳服务器相比的结果感到自豪。仍然在现代 pascal 代码和开源软件中。
不要犹豫,看看源代码,并尝试一些示例。像往常一样,欢迎在我们的论坛中
提供 反馈。