6

私はかなり古いプロジェクトを持っています:DCOMクライアントとサーバー、両方ともC ++ \ ATLにあり、Windowsプラットフォームのみです。すべてが正常に機能します。ローカルクライアントとリモートクライアントはサーバーに接続し、問題なく同時に動作します。

しかし、リモートクライアントがクラッシュしたり、タスクマネージャーによって、または「taskkill」コマンドや電源スイッチをオフにしたりして強制終了された場合、問題が発生します。私のサーバーはクライアントのクラッシュについて何も知らず、すべてのクライアントに新しいイベントを送信しようとします(すでにクラッシュしています)。その結果、一時停止し(サーバーは既にクラッシュしたクライアントにデータを送信できません)、その期間はクラッシュしたリモートクライアントの数に比例します。5回クラッシュした後、クライアントの一時停止は非常に長いため、サーバーを完全に停止することになります。

DCOMの「ping」メカニズムについて知っています(DCOMは、6分間の無音後に「2分ごとのping」に応答しないクライアントを切断する必要があります)。実際、6分間のハングの後、通常の作業が少しありますが、サーバーは「一時停止」状態に戻ります。

これらすべてで何ができますか?DCOMを「ping」で正常に動作させる方法は?独自の「ping」コードを実装する場合、古いDCOMクライアント接続を手動で切断することは可能ですか?どうやってするの?

4

4 に答える 4

1

DCOM pingシステムについてはよくわかりませんが、1つのオプションは、通知を別のスレッドプールに単純にファームオフすることです。これは、ブロックしているクライアントの数が少ない場合の影響を軽減するのに役立ちます。もちろん、クライアントが多すぎると問題が発生し始めます。

これを行う簡単な方法は、を使用することです。これQueueUserWorkItemにより、アプリケーションのシステムスレッドプールで渡されたコールバックが呼び出されます。MTAを使用していると仮定すると、これがあなたがする必要があるすべてです:

static InfoStruct {
    IRemoteHost *pRemote;
    BSTR someData;
};

static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) {
  CoInitializeEx(COINIT_MULTITHREADED);

  InfoStruct *is = (InfoStruct *)lpInfo;
  is->pRemote->notify(someData);
  is->pRemote->Release();
  SysFreeString(is->someData);
  delete is;

  CoUninitialize();
  return 0;
}

void InvokeClient(IRemoteHost *pRemote, BSTR someData) {

  InfoStruct *is = new InfoStruct;
  is->pRemote = pRemote;
  pRemote->AddRef();

  is->someData = SysAllocString(someData);
  QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION);
}

メインスレッドがSTAにある場合、これは少しだけ複雑になります。アパート間でインターフェイスポインタを使用CoMarshalInterThreadInterfaceInStreamして渡す必要があります。CoGetInterfaceAndReleaseStream

static InfoStruct {
    IStream *pMarshalledRemote;
    BSTR someData;
};

static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) {
  CoInitializeEx(COINIT_MULTITHREADED); // can be STA as well

  InfoStruct *is = (InfoStruct *)lpInfo;
  IRemoteHost *pRemote;
  CoGetInterfaceAndReleaseStream(is->pMarshalledRemote, __uuidof(IRemoteHost), (LPVOID *)&pRemote);

  pRemote->notify(someData);
  pRemote->Release();
  SysFreeString(is->someData);
  delete is;

  CoUninitialize();

  return 0;
}

void InvokeClient(IRemoteHost *pRemote, BSTR someData) {
  InfoStruct *is = new InfoStruct;
  CoMarshalInterThreadInterfaceInStream(__uuidof(IRemoteHost), pRemote, &is->pMarshalledRemote);

  is->someData = SysAllocString(someData);
  QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION);
}

わかりやすくするためにエラーチェックが省略されていることに注意してください。もちろん、すべての呼び出しをエラーチェックする必要があります。特に、RPC_S_SERVER_UNAVAILABLEその他のネットワークエラーをチェックし、問題のあるクライアントを削除する必要があります。

検討したいいくつかのより洗練されたバリエーションには、クライアントごとに一度に1つの要求のみが処理中であることを確認し(したがって、スタックしたクライアントの影響をさらに減らす)、マーシャリングされたインターフェイスポインターをMTAにキャッシュする(メインスレッドがSTA)-CoMarshalInterThreadInterfaceInStreamネットワークリクエストを実行する可能性があるため、メインスレッドでブロックされるリスクを冒すのではなく、クライアントが接続されていることがわかっている場合は、事前に処理するのが理想的です。

于 2011-01-25T15:15:04.810 に答える
0

1つの解決策は、イベントを排除することです。クライアントに、関心のあるものがあるかどうかをサーバーに照会させます。

于 2011-01-25T14:48:10.427 に答える
0

DCOMを使用して、pipeという名前の通知を確立します。切断はパイプでより適切に処理されます。リスナーは(ほ​​ぼ)即座にメッセージに応答します。例:サーバー->クライアント(パイプの名前は何ですか?)。クライアント->サーバーは、マシンを含む名前で応答します。クライアントは名前付きパイプを作成してリッスンします。サーバーは、すぐに、または必要なときにパイプを開きます。

于 2011-02-27T21:45:09.880 に答える
0

独自のpingメカニズムを実装して、クライアントがサーバーのpingメソッドを時々呼び出すようにすることができます。サーバー側でクライアント用のある種のコンテナをすでに維持しています。そのマップで、最後のpingのタイムスタンプで各クライアントをマークします。次に、クライアントにイベントを送信する前に、クライアントが動作しているかどうかを確認します。イベントの送信を停止するタイミングの戦略をカスタマイズできます。これは、失敗したpingの時間や数、イベントの種類、またはその他の要因に基づいている場合があります。おそらく、クライアントの削除について心配する必要はありません。特定のクライアントが停止していることをDCOMが認識するまで待つことができます。このスキームでは、イベントを送信する必要がある直前にクライアントが停止する可能性があるため、問題を完全に排除できない場合がありますが、ping期間を調整することで、そのようなクライアントがいくつ存在するかを完全に制御できます。

于 2011-05-29T20:28:49.767 に答える