uniGUI 超级服务器架构详解
uniGUI 超级服务器架构是一种创新的服务器设计模式,专为显著提升 uniGUI 应用的可用性、稳健性和可扩展性而构建。该架构通过整合业界成熟且广泛应用的技术,如负载均衡与进程回收机制,来实现这些目标。
传统的 uniGUI 应用服务器采用单一进程配合多线程的架构。无论是 uniGUI 可执行文件、ISAPI 还是 Apache 工作进程,所有会话都驻留在单一进程中,并通过多个线程处理传入请求。
uniGUI 超级服务器彻底革新了这一模型,转变为多进程多线程架构。在此新模式下,同一 Web 应用程序将由多个工作进程提供服务。会话将在这些进程间进行分配,根据配置,单个 uniGUI 应用程序可同时运行多个工作进程。这些进程由一个名为超级服务器进程的特殊进程进行管理和调度。超级服务器作为所有客户端请求的入口点,负责接收请求并将其分发给相应的工作进程。超级服务器还负责在需要时创建新的工作进程,并在不再需要时回收它们。由于 uniGUI 会话具有状态特性,超级服务器的另一项关键职责是将传入的会话请求定向至创建该特定会话的正确工作进程。在超级服务器术语中,每个工作进程被称为一个节点。
在深入探讨之前,我们先了解超级服务器引入的几个关键术语。
超级服务器
超级服务器是所有请求的主要入口点,它过滤请求并将它们引导至相应节点。如果请求未与特定节点关联,超级服务器将根据负载分配算法将其定向至任意节点。此外,超级服务器还负责创建新节点并在适当时回收它们。
超级服务器本身是一个专门为此目的设计的 uniGUI 应用程序,以预编译的二进制文件(Exe 和 Dll)形式部署。可用的部署选项包括:独立服务器、ISAPI 模块和 Windows 服务。未来,超级服务器组件将集成到 uniGUI 库中,使开发人员能够创建自定义的超级服务器实现。
节点
超级服务器节点实际上是一个工作进程,同时 uniGUI 应用程序本身也以独立的 EXE 模式部署。开发人员将他们的应用程序以节点形式部署。在设计节点应用程序时,开发人员无需采取任何特殊设计,与开发标准的 uniGUI 应用程序完全相同。
节点标识
每个节点都拥有一个唯一的节点标识符,在创建节点时分配。当节点被回收时,其标识符可被重新使用。
传输通道
超级服务器使用传输通道与节点进行内部通信。目前仅实现了 HTTP 传输通道。未来将基于多种技术实现其他传输方式,如 Windows 管道、TCP 和 UDP。
节点回收
节点将按照预定设计进行回收。当需要回收节点时,关联的进程将被终止。确认回收请求后,节点将开始终止其所有会话并最终终止自身进程。如果节点未确认回收请求,超级服务器将强制终止该节点。
活动节点
活动节点是那些主动服务会话的节点,能够接受新的会话请求。
暂停节点
暂停节点是无法与超级服务器通信的节点。在正常操作中,超级服务器会定期轮询所有节点。如果某个节点未能响应,则被标记为暂停状态。如果在多次重试后节点仍然无响应,则被视为失败节点并被清除。
清除节点
清除节点是从活动节点列表中移除并发送到回收队列的节点。此类节点既不接受新会话也不处理任何传入请求。当下次清理回收队列时,它将从进程空间中完全移除。
废弃节点
废弃节点是拥有多个会话但不接受新会话的节点。它将继续存在,直到所有会话终止。当没有剩余会话时,它将被清除。
持久零节点
持久零节点是标识符为 0 的特殊节点,它保证持续运行。虽然偶尔也会像其他节点一样被回收,但在回收后会立即重新加载,不会被永久卸载。持久节点的主要目的是确保至少始终有一个节点在运行。考虑一种场景:uniGUI 应用程序需要在线程中执行后台任务,这可能通过放置在 ServerModule 上的 TUniThreadTimer 实现。在传统的 uniGUI 应用程序中,只有一个进程,因此后台任务可以保证以单例形式运行。然而,在同时运行同一进程多个副本的超级服务器架构中,需要一种机制确保单例方法仅在特定节点中运行。持久零节点选项完美满足此需求。只需启用此选项并在 uniGUI 应用程序中检查节点标识符。如果 ServerModule 中名为 IsZeroNode 的布尔属性为 True,则安全执行后台任务。
以下代码示例展示了仅在当前节点为零节点(节点标识符 = 0)时执行定时器事件:
procedure TUniServerModule.ApplicationTimer(Sender: TObject);
begin
if IsZeroNode then // 仅当我是节点 #0 时执行
begin
// 在此处放置您的后台任务代码
end;
end;
或者,如果您计划使用超级服务器或传统的单进程模式部署应用程序,可以使用以下语法:
procedure TUniServerModule.ApplicationTimer(Sender: TObject);
begin
if IsZeroNode or not IsNodeMode then // 当我是节点 #0 或不是节点模式时执行
begin
// 在此处放置您的后台任务代码
end;
end;
