2

dbxユーザーセッションのWndProcにフックする方法はありますか?

背景:dbx DataSnapは、TCP通信にIndyコンポーネントを使用します。最も単純な形式では、DataSnapサーバーは接続を受け入れるIndyTCPサーバーです。接続が確立されると、Indyはその接続のスレッドを作成し、その接続のすべての要求を処理します。

これらの各ユーザー接続はリソースを消費します。同時接続数が数百のサーバーの場合、これらのリソースは高額になる可能性があります。多くのリソースをプールすることができますが、必要になるたびにリソースを取得して解放する必要はありません。

代わりに、アイドルタイマーを実装したいと思います。スレッドがリソースで終了すると、タイマーが開始します。タイマーが経過する前にスレッドがリソースにアクセスした場合でも、リソースはそのスレッドに「割り当て」られます。ただし、次のアクセスの前にタイマーが経過すると、リソースは解放されてプールに戻されます。次回スレッドがリソースを必要とするとき、別のリソースがプールから取得されます。

私はこれを行う方法を見つけていません。SetTimerを使用してみましたが、タイマーコールバックが起動しません。これは、スレッドに対するIndyのWndProcがWM_TIMERをディスパッチしていないためだと思います。このスレッドの「実行ループ」を制御できないため、イベントが通知されたかどうかを簡単に確認できません。実際、スレッドがユーザーリクエストを処理していない限り、このスレッドのコードは実行されません。そして実際、私はコードをユーザーの要求の外で実行したいと思っています。

元の質問に対する解決策または代替アプローチの提案も同様に高く評価されます。

4

2 に答える 2

1

TCP接続(HTTPトランスポートなし、したがってSessionManagerなし)を使用してユーザースレッド間でリソースを共有するための何かを実装しようとしましたが、あらゆる種類の問題が発生しました。最終的に、個々のユーザースレッド(set)の使用を中止し、 ServerContainerUnitLifeCycle := TDSLifeCycle.Serverで独自のスレッド(両方)を作成FResourcePoolFUserListましTThreadListた。実装には1日しかかからず、非常にうまく機能します。

これが私たちがしたことの単純化されたバージョンです:

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

ユーザーが接続すると、ユーザーのニーズを確認FResourcePoolします。TResource存在する場合は、リソースのUserCountプロパティをインクリメントします。UserCountユーザーが完了すると、プロパティをデクリメントして設定しLastSeenます。TTimer60秒ごとに起動するがあり、 60秒UserCount = 0LastSeen超えるリソースを解放します。

FUserList非常に似ています。ユーザーが数時間表示されていない場合は、接続が切断されたと見なされます(ユーザーが90分間アイドル状態の場合、クライアントアプリは自動切断を行うため)。そのため、サーバー側でユーザーをプログラムで切断します。 、これも各リソースの使用を減らします。もちろん、これは、セッション変数を自分で作成し(たとえばCreateGUID();)、クライアントが最初に接続したときにそれをクライアントに渡す必要があることを意味します。クライアントは、リクエストごとにセッションIDをサーバーに返すため、どのFUserListレコードがクライアントのものであるかがわかります。これはユーザースレッドを使用しないことの欠点ですが、簡単に管理できます。

于 2012-02-02T17:30:30.910 に答える
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番目が必要な場合は、メインスレッドに新しいアイテムを作成します)。

各スレッドにはリスト上のインデックスがあるため、この書き込み(TCriticalSectionでのTの呼び出し)をカプセル化する必要はありません。

メインスレッドでTTimerを使用して各スレッドのステータスを検査することで、このリストを問題なく読み取ることができます。各スレッドの終了時刻があるので、タイムアウトを計算できます。

于 2012-02-02T18:55:01.233 に答える