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

发布于 2024-12-29 03:46:44 字数 574 浏览 1 评论 0原文

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

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

每个用户连接都会消耗资源。对于具有数百个同时连接的服务器来说,这些资源可能非常昂贵。许多资源可以集中起来,但我不想每次需要时都获取和释放资源。

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

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

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

Is there a way to hook into the WndProc of a dbx user session?

Background:
dbx DataSnap uses Indy components for TCP communication. In its simplest form, a DataSnap server is an Indy TCP server accepting connections. When a connection is established, Indy creates a thread for that connection which handles all requests for that connection.

Each of these user connections consume resources. For a server with a couple hundred simultaneous connections, those resources can be expensive. Many of the resources could be pooled, but I don't want to always acquire and release a resource each time it is needed.

Instead, I'd like to implement a idle timer. After a thread finishes with a resource, the timer would start. If the thread accesses the resource before the timer has elapsed, the resource would still be "assigned" to that thread. But if the timer elapses before the next access, the resource would be released back to the pool. The next time the thread needs the resource, another resource would be acquired from the pool.

I haven't found a way to do this. I've tried using SetTimer but my timer callback never fires. I assume this is because Indy's WndProc for the thread isn't dispatching WM_TIMER. I have no control of the "execution loop" for this thread, so I can't easily check to see if an event has been signaled. In fact, none of my code for this thread executes unless the thread is handling a user request. And in fact, I'm wanting code to execute outside of any user request.

Solutions to the original question or suggestions for alternative approaches would be equally appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绿萝 2025-01-05 03:46:45

我们尝试实现一些使用 TCP 连接(没有 HTTP 传输,因此没有 SessionManager)在用户线程之间共享资源的东西,但遇到了各种各样的问题。最后,我们放弃使用单独的用户线程(设置 LifeCycle := TDSLifeCycle.Server )并创建我们自己的 FResourcePool 和 FUserList (两者都是>TThreadList)在ServerContainerUnit中。实施仅用了1天时间,效果非常好。

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

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

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

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

We tried to implement something to share resources across user threads using TCP connections (no HTTP transport, so no SessionManager), but ran into all sorts of problems. In the end we abandoned using individual user threads (set LifeCycle := TDSLifeCycle.Server) and created our own FResourcePool and FUserList (both TThreadList) in ServerContainerUnit. It only took 1 day to implement, and it works very well.

Here's a simplified version of what we did:

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

When a user connects, we check FResourcePool for the TResource the user needs. If it exists, we increment the resource's UserCount property. When the user is done, we decrement the UserCount property and set LastSeen. We have a TTimer that fires every 60 seconds that frees any resource with a UserCount = 0 and LastSeen greater than 60 seconds.

The FUserList is very similar. If a user hasn't been seen for several hours, we assume that their connection was severed (because our client app does an auto-disconnect if the user has been idle for 90 minutes) so we programmatically disconnect the user on the server-side, which also decrements their use of each resource. Of course, this means that we had to create a session variable ourselves (e.g., CreateGUID();) and pass that to the client when they first connect. The client passes the session id back to the server with each request so we know which FUserList record is theirs. Although this is a drawback to not using user threads, it is easily managed.

叹梦 2025-01-05 03:46:45

詹姆斯 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 进行检查
每个线程的状态。既然你有每个线程的结束时间
你可以计算超时。

James L maybe had nailed it. Since Indy thread does not have an message loop, you have to rely in another mechanism - like read-only thread-local properties (like UserCount and / or LastSeem in his' example) - and using main thread of the server to run a TTimer for liberating resources given some rule.

EDIT: another idea is create an common data structure (example below) which is updated each time an thread finishes its' job.

WARNING: coding from mind only... It may not compile... ;-)

Example:

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;

Put it in a list (any list class you like), and make sure each thread is associated
with a index in that list. Removing items from the list only when you won't use that
number of threads anymore (shrinking the list). Add an item when you create a new thread
(example, you have 4 threads and now you need an 5th, you create a new item on main thread).

Since each thread have an index on the list, you don't need to encapsulate this write (the
calls on T
on a TCriticalSection.

You can read this list without trouble, using an TTimer on main thread to inspect
the status of each thread. Since you have the time of each thread's finishing time
you can calculate timeouts.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文