【问题标题】:DCOM: How to close connection in server on client crash?DCOM:如何在客户端崩溃时关闭服务器中的连接?
【发布时间】:2011-06-15 06:00:53
【问题描述】:

我有一个相当老的项目:DCOM 客户端和服务器,都在 C++\ATL 中,只有 Windows 平台。一切正常:本地和远程客户端连接到服务器并同时工作,没有任何问题。

但是当远程客户端崩溃或被任务管理器或“taskkill”命令或电源关闭时 - 我遇到了问题。我的服务器对客户端崩溃一无所知,并尝试向所有客户端发送新事件(也已崩溃)。结果我暂停了(服务器无法将数据发送到已经崩溃的客户端),它的持续时间与崩溃的远程客户端的数量成正比。 5 次崩溃后的客户端暂停时间过长,等于服务器完全停止。

我知道 DCOM 的“ping”机制(DCOM 应该在 6 分钟静默后断开不响应“每 2 分钟 ping”的客户端)。真的,在挂起 6 分钟后,我有一小段时间可以正常工作,但随后服务器又回到了“暂停”状态。

我能用这一切做什么?如何使 DCOM“ping”正常工作?如果我将实现自己的“ping”代码,是否可以手动断开旧的 DCOM 客户端连接?怎么做?

【问题讨论】:

  • 您是否考虑过从线程池线程发送事件,以在一定程度上缓解阻塞?
  • @bdonlan:这可能是一个解决方案,但是这会使服务器变得非常复杂——它必须处理那些额外的线程生命周期。
  • 不是真的——你可以只使用内置的win32线程池。如果您已经在使用 MTA,那么点击 QueueUserWorkItem 非常简单。如果您在 STA 中,则必须将远程接口的句柄编组到 MTA 中,但这仍然不是太难使用 CoMarshalInterThreadInterfaceInStream 等
  • 事实上,也许我会把这个写成一个正确的答案:)
  • 这是 DCOM 的根本致命弱点,通常是中间件。将网络抽象出来在实践中效果不佳。重新构建具有单点故障的松散耦合系统既痛苦又昂贵。查看消息队列。

标签: c++ atl dcom


【解决方案1】:

我不确定 DCOM ping 系统,但您可以选择将通知发送到单独的线程池。这将有助于减轻拥有少量阻塞客户端的影响 - 当然,当有太多阻塞客户端时,您就会开始遇到问题。

执行此操作的简单方法是使用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 中,这只会稍微复杂一些;你只需要使用CoMarshalInterThreadInterfaceInStreamCoGetInterfaceAndReleaseStream 在公寓之间传递接口指针:

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 和其他此类网络错误,并删除有问题的客户端。

您可能需要考虑一些更复杂的变体,包括确保每个客户端一次只有一个请求在进行中(从而进一步减少客户端卡住的影响)和缓存 MTA 中的编组接口指针(如果您的主thread 是一个 STA)- 因为我相信 CoMarshalInterThreadInterfaceInStream 可能会执行网络请求,所以您最好在 知道 客户端已连接时提前处理它,而不是冒着阻塞的风险你的主线程。

【讨论】:

    【解决方案2】:

    一种解决方案是消除事件 - 让客户端查询服务器是否有任何感兴趣的东西。

    【讨论】:

    • 真的,不可能。我有数百个事件,尽快通知客户有关事件非常重要。如果客户端甚至每 1 秒都会向服务器询问新事件 - 这还不够快。如果客户端每秒询问服务器 100 次 - 超过 5 个客户端将完全挂起服务器、网络和 CPU。
    • @Ezh:听起来很合理,但你实际测试过吗?一百万个“什么都不做”调用的执行速度有多快?
    • 是的,我已经测试过了。远程 DCOM 调用它不是“什么都不做”。它是网络通信、Windows 安全、编组等。几毫秒加上网络和 CPU 负载。
    • @Ezh:我的意思是在服务器端实现中“不做”,以便可以衡量其自身的开销。所以你说一百万次调用需要几毫秒?
    • 在这篇文章 (technet.microsoft.com/en-us/library/cc722925.aspx) 中有 Microsoft 的 DCOM 方法调用基准。很快 - 每次远程调用需要 2-3 毫秒。
    【解决方案3】:

    使用 DCOM 建立一个名为管道的通知。 使用管道可以更好地处理断开连接。 听众(几乎)立即响应消息。 例如服务器-> 客户端(您的管道名称是什么?)。 Client->Server 以包含机器的名称响应。 客户端创建命名管道并监听。 服务器立即或在需要时打开管道。

    【讨论】:

      【解决方案4】:

      您可以实现自己的 ping 机制,以便您的客户端不时调用服务器的 ping 方法。您已经在服务器端为您的客户端维护了某种容器。在该地图中,用最后一次 ping 的时间戳标记每个客户端。然后在向该客户端发送事件之前检查客户端是否处于活动状态。 您可以自定义何时停止发送事件的策略,可能基于时间或错过的 ping 次数或事件类型或其他一些因素。 您可能不需要担心删除客户端 - 可以等到 DCOM 意识到特定客户端已死。 此方案可能无法完全消除问题,因为客户端可能会在需要发送事件之前死亡,但您可以通过调整 ping 周期完全控制可能存在的此类客户端的数量。这段时间越短,死客户就越少,尽管您是用流量付费的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-28
        相关资源
        最近更新 更多