【问题标题】:IdHTTPserver: Share ADOConnectionIdHTTPserver:共享 ADOConnection
【发布时间】:2011-09-16 15:25:54
【问题描述】:

我正在使用 TIdHTTPServer 创建一个简单的 Indy 网络服务器。在几乎所有请求中,服务器都需要与数据库进行通信(通过 TAdoConnection)。看到数据库连接在资源方面有点贵,想创建一个池化机制来复用连接;而不是在每个请求上建立连接。

我没有成功搜索示例。 Embarcadero 论坛上的This Link 建议对 TIdSchedulerOfThreadPoolTIdThreadWithTask 的后代进行子分类。但我仍然无法将它们全部组合在一起。

我是否需要重写 TIdSchedulerOfThreadPool.NewThread 方法并让它返回我的子类 TIdThreadWithTask 对象,而该对象又会有它自己的 TAdoConnection强> 对象?

有人有例子吗?我应该不用担心这个,只需在每个请求上打开数据库连接吗?

【问题讨论】:

    标签: delphi connection-pooling indy


    【解决方案1】:

    你为什么不自己管理游泳池?

    您有一个连接列表。它开始是空的。每次请求到来时,您都会查找可用连接(处于活动状态但未使用的连接)。如果没有找到,则创建一个并将其放入列表中,因为不可用。请求结束后,您将连接设置为可用。

    去过那里,做到了,一点也不后悔!几个关注点:

    • 始终牢记线程安全:
      • 查询列表时
      • 当您查询连接的可用性时
      • 当您设置连接的可用性时
    • 不时检查您是否有太多未使用的可用连接,可能超出您的需要,因此必须将它们设置为不可用,从列表中删除然后关闭。

    【讨论】:

    • 这听起来很简单。因此,创建一个 TMyConnectionObject 类,该类将具有 TAdoConnection 对象和一个布尔值“Active”。我有一个 GLOBAL TStringList 或 TList 来存储它们。当 OnCommandGet 被触发时,我会遍历所有对象以找到“InActive”对象。如果有我用它;否则创建并添加。是否有可能同时触发 OnCommandGet 并且我的处理会拾取相同的可用对象?
    • 为了牢记线程安全,我是否只使用关键部分?
    • 你明白了。阅读我的更新:两个连接线程有可能同时尝试访问该列表。您必须使用线程特定策略:线程安全列表 (docwiki.embarcadero.com/CodeExamples/en/TThreadList_(Delphi))、CriticalSection 等。您可能还想在这里阅读:eonclash.com/Tutorials/Multithreading/MartinHarvey1.1/ToC.html
    • 非常感谢!有时我让自己被更“雄辩”/“学术”的方式引导,而有更简单的方法,只需一点编码。仅供参考 - 上面的 dockwiki 链接需要在 url 中添加正确的括号。
    • 我会让 ADO 管理它自己的池,而不是创建我自己的......只是我的 2 美分。
    【解决方案2】:

    您还必须确保在每个线程中正确设置 COM,并调用该对:CoInitialize/CoUnitialize

    以下是要包含在您的项目中的示例单元。在您的 HTTP 服务器构造函数中,只需创建您的自定义调度程序,Indy 将使用它而不是默认值。

    如果您这样做,那么每个客户端线程都将针对 COM 正确初始化,您还可以添加其他将由所有客户端线程共享的项目。

    我还为每个连接创建了一个自定义的 TIdServerContext 后代(并在 HTTP Server 构造函数中设置了 ContextClass 属性。)不同类型的服务器有不同的 TIdServerContext 后代,但它们都使用 TsoIndyCOMEnabledSchedulerOfThread 基线程类,因为它们都这样做某种COM。

    我不会将 ADO 连接放入线程中,而是放入上下文中……尤其是如果您将其进一步放入线程池中。

    unit ExampleStackOverflow;
    
    interface
    
    uses
      SysUtils, Classes,
      ActiveX,
      IdThread, IdSchedulerOfThreadDefault;
    
    type
      //Meant to be used with a custom TIdSchedulerOfThreadDefault descendant
      //to ensure COM support on child threads.
      TsoIndyComThreadWithTask = class(TIdThreadWithTask)
      protected
        //Ensure COM is setup before client connection/thread work
        procedure BeforeExecute; override;
        //Graceful COM cleanup on client connection/thread
        procedure AfterExecute; override;
      end;
    
    
      TsoIndyCOMEnabledSchedulerOfThread = class(TIdSchedulerOfThreadDefault)
      public
        constructor Create(AOwner:TComponent); reintroduce;
      end;
    
    
    
    implementation
    
    procedure TsoIndyComThreadWithTask.BeforeExecute;
    begin
      CoInitialize(nil);
      inherited;
    end;
    
    
    procedure TsoIndyComThreadWithTask.AfterExecute;
    begin
      inherited;
      CoUninitialize();
    end;
    
    
    constructor TsoIndyCOMEnabledSchedulerOfThread.Create(AOwner:TComponent);
    begin
      inherited;
      //the whole reason for overriding default scheduler of thread is to setup COM
      //on client threads
      ThreadClass := TsoIndyComThreadWithTask;
      Name := Name + 'COMEnabledScheduler';
    end;
    

    【讨论】:

    • 感谢您的详细回答。我将不得不尝试消化它。这是我没有很好处理的东西。
    • 有一点是肯定的......不要“暂停”具有打开数据库连接的线程。正如我之前评论的那样,我会让微软管理 ADO 连接池,因为他们已经做得很好了。我会在每个 Context 上创建/释放一个 ADOConnection - 默认情况下它会被 Microsoft 池化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    • 1970-01-01
    • 2018-01-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多