挂钩dbx DataSnap用户会话的消息循环

时间:2012-01-25 21:04:28

标签: delphi delphi-xe datasnap indy10

有没有办法挂钩到dbx用户会话的WndProc?

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

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

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

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

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

2 个答案:

答案 0 :(得分:1)

我们尝试使用TCP连接(没有HTTP传输,因此没有SessionManager)实现跨用户线程共享资源的东西,但遇到了各种各样的问题。最后,我们放弃了使用单个用户线程(设置LifeCycle := TDSLifeCycle.Server)并在 ServerContainerUnit 中创建了我们自己的FResourcePoolFUserList(两个TThreadList)。它只需要1天就可以实现,而且效果非常好。

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

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

当用户连接时,我们会检查FResourcePool是否有用户需要的TResource。如果存在,我们增加资源的UserCount属性。用户完成后,我们递减UserCount属性并设置LastSeen。我们有一个TTimer,每60秒触发一次,可以释放任何资源UserCount = 0LastSeen超过60秒。

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

答案 1 :(得分:1)

詹姆斯L也许已经钉了它。由于Indy线程没有消息循环,你必须依赖另一种机制 - 比如只读线程局部属性(如 UserCount 和/或 LastSeem 在他的'示例) - 并使用服务器的主线程来运行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进行检查 每个线程的状态。因为你有每个线程的完成时间 你可以计算超时。