【问题标题】:Delphi 7 Indy 9 multithreaded HTTP ServerDelphi 7 Indy 9 多线程 HTTP 服务器
【发布时间】:2014-06-29 13:28:47
【问题描述】:

我需要编写一个多线程 HTTP 服务器。我知道 Indy 通过 IdThreadMgrPool 处理线程。

我的要求很简单:有一个服务器可以同时接受多个客户端(Web 浏览器 POST 请求),运行有限数量的调用 Delphi DLL 的线程(可能独立于连接线程),并返回结果。

非阻塞将是终极的(如 node.js)。

关于这个主题有几个 SO 帖子。我在这里(或通过 Google)找不到的是如何使用 IdThreadMgrPool 的示例。我看到帖子说必须使用它,但没有示例如何使用。 Indy 的网站上也没有示例。

有人能给我举个例子吗?或者是否有适用于此的 FOS 框架?

一个想法是让 Indy 正常生成多个线程,然后让这些线程访问运行 DLL 的受控线程池。如果这是合理的,那么我只需要知道“正常”案例用法示例。

【问题讨论】:

  • 每个 HTTP 请求都会根据需要自动生成新线程。我认为您根本不需要线程池,我从来没有使用过。
  • 那我该如何控制被调用的DLL实例的数量呢?如果我说的是双 CPU 服务器,我希望初始化 2 个 DLL 实例并等待客户端。我不想每次都启动 DLL 并杀死它(它是一个处理引擎并且有开销)。
  • 这使得它非常棘手。您不应该对每个请求都执行此操作,因为假设服务器同时接收 10 个请求。它可能需要自动生成多达 10 个线程来同时处理所有线程。您不想将 Indy 限制为仅生成 2 个线程,否则它将无法按预期同时工作。在接收任何请求之前,您需要通过其他方式控制此 DLL。
  • 当已经有许多非常好的 HTTP 服务器可以为您处理这些问题时,您是否有某些原因要编写自己的服务器?特别是,如果您在 Windows 上,如果您编写 Delphi ISAPI dll,IIS 7.5 将使您对所询问的内容进行精细控制。
  • 听取@Vector 的好建议。让 IIS 处理服务,并根据需要调用尽可能多的 DLL 实例。我有一个可以全天处理每秒 40 个请求的请求。我不担心线程、印地等。

标签: web-services delphi webserver indy httpserver


【解决方案1】:

Indy 服务器无法很好地扩展。

考虑使用基于 iocp 的 http.sys 内核模式服务器。

我们的开源 SynCrtSock 单元具有高性能的 http 服务器,并且可以从 Delphi 5 到 XE6 完美运行。

例如见this sample code

【讨论】:

    【解决方案2】:

    如果您有可用的 Windows 服务器,我建议您使用 Delphi 7 编写一个 ISAPI DLL 并将其部署在 IIS 上,这将处理您的所有服务器端问题,特别是如果您有带有 IIS 7.5(或更高版本)的 Server 2008 R2。您的客户端可以使用 Indy 连接到 IIS,Indy 客户端与 Delphi ISAPI 的请求/响应模型相结合,支持多种功能,可以轻松地将数据从客户端传递到服务器端请求处理程序并返回到客户端。 不需要 ASP.NET - 所有原生 Delphi 和“经典”ISAPI 应用程序模型。

    Delphi 7 将生成 ISAPI 应用程序代码 - 当 IIS 将请求发送到您的 DLL 时,您将获得一个操作编辑器来处理请求,这取决于您的客户端发送的 URL。您可以保存会话信息、创建全局缓存,并且您基本上可以在 ISAPI DLL 的上下文中使用 VCL 的所有非可视方面。

    IIS 处理线程 - 可能会处理比您需要的多得多的同时连接 - 并且还使您能够确定一次可以加载多少个 DLL 实例,以及许多其他选项,例如回收,各种形式的身份验证和安全级别。您可以使用 IIS 的应用程序池功能进行扩展等。

    我已经使用这种架构开发了几个大型企业级解决方案(Indy 客户端嵌入在 Delphi exe 中)。一旦您了解了 Delphi 的 ISAPI 应用程序模型的基础知识和 IIS 部署的错误,它就很稳定并且相对“无痛”。

    如果您有这个选项,为什么要重新发明轮子?

    【讨论】:

    • 我有这个选项可用。我会去的,谢谢。
    • @IanC - 最棘手的部分是在 IIS 中正确部署。有很多设置和选项。大部分内容都记录在 MS docs online 和 here on SO 中,包括我提出的一些问题。
    • 我在 IIS 上设置了一个 VB 应用程序。这不好玩。我一定会仔细查看您的问题,谢谢。
    • @IanC -Delphi7 具有您可能想要避免的“WebBroker”(或类似的)框架——它是一种时髦的专有解决方案,而且非常过时。在 ISAPI 应用程序框架和动作编辑器之外,我从未使用过它。您可以像在桌面应用程序中一样执行所有操作,并使用 ISAPI 和 Indy 将文本或数据发送回您的客户端。您可以使用 ADO 在服务器上运行数据库查询,将结果加载到 TClientDataSet 中,作为 XML 发回并使用 XML 在客户端上加载另一个实例,还可以抓取、显示和编辑它。 KBMMemtable 还支持流式二进制数据。
    【解决方案3】:

    要使用TIdThreadMgrPool,您至少要做的就是创建它的一个实例,将其分配给TIdHTTPServer.ThreadMgr 属性,然后设置它的PoolSize 属性。所有这些都可以在设计时完成。

    请记住,PoolSize 不限制服务器上的连接数。为此,服务器有自己的MaxConnections 属性。例如,您可以有 10 个 PoolSize 并同时连接 15 个客户端,从而运行 15 个线程。当它们断开连接时,10 个线程将被放回池中,5 个线程将被终止。

    要自定义池线程,您可以从 TIdPeerThread 派生一个新类,可选择覆盖其虚拟 BeforeExecute()AfterExecute() 方法以执行每个线程的初始化清理,然后将该类分配给服务器的 (不是 ThreadMgr 的)ThreadClass 属性在运行时激活服务器之前。在您的服务器事件处理程序中,您可以将提供的 TIdPeerThread 对象类型转换为您的自定义类并根据需要使用它。

    您可以向自定义线程类添加方法,并让它们在内部访问 DLL,并根据需要进行限制。最简单的限制是使用单个共享信号量来控制一次可以进入信号量的线程数。在这方面,即使有 15 个线程正在运行,您也可以一次限制为 2 个线程。

    既然您说要“在线程中运行 DLL”,那么信号量可能还不够。在这种情况下,我建议改用I/O Completion Port。您可以让您的自定义线程类使用PostQueuedCompletionStatus() 向 IOCP 发布请求并等待响应返回。节流是通过您为服务 IOCP 而创建的线程数来完成的,例如每个 CPU 内核一个线程。每个 IOCP 线程都会在循环中使用GetQueuedCompletionStatus() 来接收发布的请求。

    Indy 不是异步的,因此您无法将请求发布到 IOCP 并让它在准备好后直接将响应发送回客户端。服务器使用管理客户端连接的同一线程将响应发送回客户端。因此,客户端线程必须向 IOCP 发送请求并等待其响应,然后将该响应发送给客户端。您可以定义一条记录,其中包含TEvent、调用 DLL 所需的输入值以及 DLL 响应的输出值。然后创建该记录的一个实例,将指向它的指针发布到 IOCP,并等待TEvent 发出信号。当 IOCP 线程接收到记录指针时,它可以根据需要调用 DLL,用响应填充记录,然后向记录的TEvent 发出信号。等待的客户端线程将被解除阻塞,并可以根据需要将记录的响应数据发送给客户端。

    【讨论】:

    • 谢谢@Remy。您碰巧没有指向示例的链接。总是最容易看到工作代码。
    【解决方案4】:

    在此处查看示例: http://sourceforge.net/p/xxm/code/HEAD/tree/trunk/Delphi/http/

    线程池逻辑在这里: http://sourceforge.net/p/xxm/code/HEAD/tree/trunk/Delphi/common/xxmThreadPool.pas

    Xxm 实际上提供了一个您可以编写代码的接口,因此结果可以通过 IIS、Apache 或具有多线程的 plain HTTP 服务器进行移植。还有一个 http.sys 版本和一个连接器,可以在本地运行到 Internet Explorer(非常适合调试)。每个都有一个“自动更新”版本,它将热交换项目 DLL 并将其用于任何新请求(非常适合实时服务器)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-29
      • 1970-01-01
      • 2021-11-22
      • 1970-01-01
      • 2012-07-12
      • 1970-01-01
      • 2017-02-15
      • 1970-01-01
      相关资源
      最近更新 更多