2

有没有办法连接到 dbx 用户会话的 WndProc?

背景:dbx DataSnap 使用 Indy 组件进行 TCP 通信。在最简单的形式中,DataSnap 服务器是一个接受连接的 Indy TCP 服务器。建立连接后,Indy 会为该连接创建一个线程来处理该连接的所有请求。

这些用户连接中的每一个都消耗资源。对于具有几百个同时连接的服务器,这些资源可能很昂贵。许多资源可以被池化,但我不想总是在每次需要时获取和释放资源。

相反,我想实现一个空闲计时器。在线程完成资源后,计时器将启动。如果线程在计时器结束之前访问资源,则资源仍将“分配”给该线程。但是,如果在下一次访问之前计时器已过,则资源将被释放回池中。下次线程需要资源时,将从池中获取另一个资源。

我还没有找到办法做到这一点。我试过使用 SetTimer 但我的计时器回调永远不会触发。我认为这是因为 Indy 的线程 WndProc 没有调度 WM_TIMER。我无法控制该线程的“执行循环”,因此我无法轻松检查是否已发出事件信号。事实上,除非线程正在处理用户请求,否则我的这个线程的代码都不会执行。事实上,我希望代码在任何用户请求之外执行。

对原始问题的解决方案或替代方法的建议将同样受到赞赏。

4

2 回答 2

1

我们尝试使用 TCP 连接(没有 HTTP 传输,所以没有 SessionManager)实现跨用户线程共享资源,但遇到了各种各样的问题。最后我们放弃了使用单独的用户线程(set )并在ServerContainerUnitLifeCycle := TDSLifeCycle.Server中创建了我们自己的FResourcePoolFUserList(both TThreadList)。实施只用了1天,效果很好。

这是我们所做工作的简化版本:

TResource = class
  SomeResource: TSomeType;
  UserCount: Integer;
  LastSeen: TDateTime;
end;

当用户连接时,我们会检查FResourcePool用户TResource的需求。如果它存在,我们增加资源的UserCount属性。当用户完成后,我们减少UserCount属性并设置LastSeen。我们有一个TTimer每 60 秒触发一次的 a,它释放 aUserCount = 0LastSeen大于 60 秒的任何资源。

FUserList非常相似。如果几个小时没有看到用户,我们假设他们的连接被切断(因为如果用户空闲 90 分钟,我们的客户端应用程序会自动断开连接)所以我们以编程方式断开服务器端的用户,这也减少了他们对每种资源的使用。当然,这意味着我们必须自己创建一个会话变量(例如CreateGUID();),并在客户端第一次连接时将其传递给客户端。客户端通过每个请求将会话 ID 传递回服务器,以便我们知道哪条FUserList记录是他们的。尽管这是使用用户线程的一个缺点,但它很容易管理。

于 2012-02-02T17:30:30.910 回答
1

詹姆斯 L 可能已经成功了。由于 Indy 线程没有消息循环,因此您必须依赖另一种机制 - 例如只读线程本地属性(例如他的示例中的UserCount和/或LastSem ) - 并使用服务器的主线程来运行 TTimer在给定一些规则的情况下解放资源。

编辑:另一个想法是创建一个通用数据结构(下面的示例),每次线程完成其工作时都会更新它。

警告:仅凭头脑编码...它可能无法编译... ;-)

例子:

TThreadStatus = (tsDoingMyJob, tsFinished);

TThreadStatusInfo = class
private
  fTStatus : TThreadStatus;
  fDTFinished : TDateTime;
  procedure SetThreadStatus(value: TThreadStatus);
public
  property ThreadStatus: TThreadStatus read fTStatus write SetStatus;
  property FinishedTime: TDateTime read fDTFinished;
  procedure FinishJob ;
  procedure DoJob;
end

procedure TThreadStatusInfo.SetThreadStatus(value : TThreadStatus)
begin
  fTStatus = value;
  case fTStatus of 
    tsDoingMyJob :
       fDTFinished = TDateTime(0);
    tsFinished:
       fDTFinished = Now;
  end;
end;

procedure TThreadStatusInfo.FinishJob;
begin
  ThreadStatus := tsFinished;
end;

procedure TThreadStatusInfo.DoJob;
begin
  ThreadStatus := tsDoingMyJob;
end;

将它放入一个列表(您喜欢的任何列表类)中,并确保每个线程都与该列表中的索引相关联。仅当您不再使用该数量的线程时才从列表中删除项目(缩小列表)。当你创建一个新线程时添加一个项目(例如,你有 4 个线程,现在你需要第 5 个,你在主线程上创建一个新项目)。

由于每个线程在列表中都有一个索引,因此您不需要封装此写入(对 T 上的调用 TCriticalSection.

您可以毫无困难地阅读此列表,在主线程上使用 TTimer 来检查每个线程的状态。由于您有每个线程的完成时间,因此您可以计算超时。

于 2012-02-02T18:55:01.233 回答