【问题标题】:How to release DataSnap memory once connections are closed?连接关闭后如何释放 DataSnap 内存?
【发布时间】:2013-07-18 20:52:18
【问题描述】:

我有一个作为服务 [基于会话的生命周期] 运行的 TCP/IP DataSnap 服务器,它不断地消耗内存,即使没有连接到它,也永远不会恢复到起始内存大小。

为了消除我的代码作为罪魁祸首,我已经建模了一个基本的 TCP/IP DataSnap 服务器,作为 VCL [基于会话的 LifeCycle] 运行,它服务于一个服务器方法类 [TDSServerModule],它只包含使用本机数据的基本数学函数types [没有要创建或释放的对象]。

当我使用非常瘦的客户端连接到所述 DataSnap 服务器时,我得到了相同的结果。 内存使用随着每个连接不断增长,并且在从客户端执行服务器端方法时偶尔增长。一旦连接关闭,DataSnap 服务器永远不会减少其内存使用量[即使在没有连接的情况下运行 8 小时]。

关于为什么会发生这种情况或更重要的是如何减少它的任何建议?

我正在使用 RAD Studio XE2 Update 4 HotFix 1。

【问题讨论】:

  • Datasnap 已死。数据快照万岁!

标签: delphi memory-leaks delphi-xe2 datasnap


【解决方案1】:

让我引用一篇关于 DataSnap 的“必读”文章。这是关于 XE3 但我希望这里的代码也适用于 XE2。

内存消耗

我观察到的一个问题与内存消耗有关。如果调用的方法完全不执行任何操作,为什么 Datasnap 服务器会消耗这么多内存?

也许我不知道如何准确解释,但我会尝试。基本上,DataSnap 会为它接收到的每个 HTTP 连接创建一个会话。这个会话会在20分钟后被销毁,也就是说,在测试的前20分钟内存消耗只会增加,之后有趋于稳定的趋势。我真的不知道为什么 Datasnap 会这样做。在 REST 应用程序中,我认为使用默认配置的这些会话没有多大意义。当然,会话可能会有所帮助,但我不明白为什么它是默认配置。事实上,DataSnap 并没有这样的配置。看起来您只需要使用此会话控件,而无法选择其他方式(没有文档)。 MORMot 框架也有会话控制,但它是可配置的,不会消耗太多内存。

无论如何,有办法解决这个问题。 Daniele Teti 在他的博客上写了一篇文章,看看。我将在这里展示的解决方案是由他放在他的博客上的。谢谢丹尼尔。

uses System.StrUtils, DataSnap.DSSession, Data.DBXPlatform;

function TServerMethods1.HelloWorld: String;
 begin

 Result := 'Hello World';
 GetInvocationMetaData.CloseSession := True;
end;

运行此方法后,会话将关闭,内存消耗会降低。当然,创建和销毁这个会话仍然存在开销。

因此,如果在 XE2 中可行的话,对您来说最好的方法似乎是使用显式内存清理来结束每个服务器方法。那你最好再读一遍这些文章,为未来的可扩展性挑战做好准备。

【讨论】:

  • 感谢您的链接。我已经实现了 'GetInvocationMetaData.CloseSession := True;'调用我所有的服务器方法,每次方法调用的内存消耗似乎略有减少。然而,DataSnap 服务器似乎仍然在每个连接都消耗内存并且永远不会释放它。
  • 那么也许你最好放弃datasnap
【解决方案2】:

我添加了以下方法并从“TWebModule1::WebModuleBeforeDispatch”事件中调用它。它消除了内存消耗,实际上允许空闲的 REST 服务返回到无会话内存的状态。 DataSnap 肯定需要解决这个问题。

// ---------------------------------------------------------------------------
/// <summary> Memory Restoration. DataSnap opens a session for each call
///         even when the service is set for invocation.
///         Sessions are building up consuming memory and seem not to be freed.
///         See: https://stackoverflow.com/questions/17748300/how-to-release-datasnap-memory-once-connections-are-closed
/// </summary>
/// <remarks> Iterates session in the session manager and closes then terminates
///     any session that has been idle for over 10 seconds.
/// </remarks>
/// <returns> void
/// </returns>
// ---------------------------------------------------------------------------
void TWebModule1::CloseIdleSessions()
{
TDSSessionManager* sessMgr = TDSSessionManager::Instance;
int sessCount = sessMgr->GetSessionCount();
WriteLogEntry(LogEntryTypeDebug, "TWebModule1::CloseIdleSessions", "Session Count: " + IntToStr(sessCount));
TStringList* sessKeys = new TStringList;
sessMgr->GetOpenSessionKeys(sessKeys);
WriteLogEntry(LogEntryTypeDebug, "TWebModule1::CloseIdleSessions", "Session Keys Count: " + IntToStr(sessKeys->Count));
TDSSession* sess = NULL;
for(int index = 0; index < sessKeys->Count; index++)
{
    String sessKey = sessKeys->Strings[index];
    sess = sessMgr->Session[sessKey];
    unsigned elapsed = (int)sess->ElapsedSinceLastActvity();
    if(elapsed > 10000)
    {
        WriteLogEntry(LogEntryTypeDebug, "TWebModule1::CloseIdleSessions", "CloseSession TerminateSession Key: " + sessKey);
        sessMgr->CloseSession(sessKey);
        sessMgr->TerminateSession(sessKey);
    }
    sess = NULL;
}
delete sessKeys;
sessMgr = NULL;
}

【讨论】:

  • 注意:我在这个函数的调用周围使用了一个 MUTEX 来防止多个线程尝试对同一个会话进行操作。
  • 谢谢丹,这是一个很好的解决方案。还有一个想法:我正在检查会话状态,如有必要,我将关闭会话,因为在我的应用程序(Delphi 10.3,linux Datasnap 服务器)中,“ElapsedSinceLastActvity”似乎无法正常工作。我还在代码中将 TerminateSession 替换为 RemoveSession。
  • 嗨,Dan,还有一件事,在 Delphi 10.3 中,仅使用 closesession 就足够了,因为它也已经执行了 removesession。
【解决方案3】:

您应该检查服务器容器上 TDSServerclass 组件的 Lifecycle 属性。它提供了一种确定会话处理方式的方法。它默认为会话。将其设置为调用将在每次调用(调用)后释放会话。这当然意味着你没有状态。不过,这在典型的 REST 服务器中是可以的。

如果您的内存消耗仍在增长。将以下行放入您的 dpr 单元中。 ReportMemoryLeaksOnShutdown := True;
然后,您的应用程序将向您显示它在关闭数据快照服务器时发生的内存泄漏。

【讨论】:

  • 如问题中所述,我已将生命周期设置为会话。我必须使用 Session 因为调用对数据库服务器 [Oracle 11g] 征税。我已经尝试了 ReportMemoryLeaksOnShutdown 函数,但 DataSnap 没有报告任何内容。当服务器关闭并报告泄漏时,我故意让对象在内存中保持活动状态。所以我不认为是我的代码导致了内存增长,我认为它纯粹是 DataSnap。
猜你喜欢
  • 2013-02-11
  • 1970-01-01
  • 2020-01-04
  • 2012-07-11
  • 1970-01-01
  • 1970-01-01
  • 2012-03-15
  • 2013-01-02
  • 2011-09-18
相关资源
最近更新 更多