65.9K
CodeProject 正在变化。 阅读更多。
Home

TCP Remoting Windows 服务主机,带 Server GC

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.40/5 (3投票s)

2004年4月15日

CPOL

4分钟阅读

viewsIcon

68479

downloadIcon

690

为 TCP 远程宿主实现服务器 GC。

引言

The Server Side 发布了 Microsoft Pattern and Practice Group 的《Improving .NET Application Performance and Scalability》(提高 .NET 应用程序性能和可伸缩性)的 beta 版本。在关于远程处理的第 8 章中,将远程处理的 Windows 服务宿主标记为未优化,因为它使用了工作站 GC(垃圾回收)。此外,根据 Gregor Noriskin 在 这篇文章中的说法:“服务器 GC 针对吞吐量和多处理器可伸缩性进行了优化”,并且“如果您正在构建将在多处理器计算机上运行的服务器应用程序,强烈建议您使用服务器 GC”。

本文旨在实现一个带服务器 GC 的 TCP 远程宿主。源代码包含在内,供社区中的开发人员评论和改进。我将尝试逐步解释此宿主的构建方式,并希望能引起大家对编写真正可伸缩的 TCP 远程宿主的兴趣。

本文主要面向具有 C# 和 .NET Framework(远程处理)经验的开发人员。ATL 的使用是初级水平,仅用于创建 CLR 宿主。

如何指定服务器 GC

必须在加载 AppDomain 和执行用户代码之前指定服务器 GC。因此,我们将不得不使用非托管代码。以下是 MSDN 文档中用于此目的的 API:

HRESULT hr = 
CorBindToRuntimeEx(pszVer, pszFlavor, Flags, CLSID_CorRuntimeHost, 
IID_ICorRuntimeHost, (void **)&pHost);

根据 这篇文章,对于 CLR 服务器版本,`pszFlavor` = "svr",对于 CLR 工作站版本,为 "wks",而 CLR 服务器版本旨在利用多个处理器并具有更好的可伸缩性。至于 `Flags`,`STARTUP_CONCURRENT_GC` 代表并发 GC,而其他所有内容代表非并发 GC。非并发 GC 在与用户代码相同的线程上执行收集,服务器应用程序对执行用户代码的特定请求响应会较慢,但整体 GC 收集性能更好。因此,对于作为服务器应用程序运行的 TCP 远程宿主,我选择使用以下 API:

LPWSTR pszVer = L"v1.1.4322";
LPWSTR pszFlavor = L"svr";
ICorRuntimeHost *pHost = NULL;  
// this is changed to a global unmanaged variable in download code

HRESULT hr = CorBindToRuntimeEx(pszVer, pszFlavor,  
STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN, CLSID_CorRuntimeHost, 
IID_ICorRuntimeHost, (void **)&pHost);

显然,我已将服务器 GC 定义为“带非并发 GC 的 CLR 服务器版本”,并且我相信服务器 GC 具有更好的可伸缩性,并且可以更好地利用多个 CPU。

如何获取默认 AppDomain

在创建上述 CLR 宿主后,我们需要访问其默认 AppDomain 以加载我们的远程宿主代码。事实上,所有托管代码都必须驻留在 AppDomain 中,而 CLR 宿主的默认 AppDomain 是最简单的选择。

pHost->Start();
mscorlib::_AppDomain *pDefaultDomain = NULL;
IUnknown   *pAppDomainPunk = NULL;

hr = pHost->GetDefaultDomain(&pAppDomainPunk);
hr = pAppDomainPunk->QueryInterface(__uuidof(mscorlib::_AppDomain), 
                                      (void**) &pDefaultDomain);

请注意,`pDefaultDomain` 是来自 *mscorlib.tlb* 的 COM 指针类型,它允许我们从非托管 CLR 宿主中访问默认 AppDomain。接下来,我们进入托管代码,并将我们的托管远程宿主代码加载到此默认 AppDomain 中,如下所示:

JQD::RemoteObjectLoader *pRemotingHost=NULL;
mscorlib::_ObjectHandle *pObjHandle = NULL;
hr = pDefaultDomain->CreateInstance(_bstr_t("RemoteObjectLoader"), 
                                 _bstr_t("JQD.RemoteObjectLoader"),       
                                  &pObjHandle); 

VARIANT v;
VariantInit(&v);
hr = pObjHandle->Unwrap(&v);
hr =v.pdispVal->QueryInterface(
                     __uuidof(RemoteObjectLoader::_RemoteObjectLoader), 
                    (void**) &pRemotingHost);
pRemotingHost->Load();

请注意,指向默认 AppDomain 的指针会创建一个具有非托管句柄的托管对象实例,该句柄可以解开并转换为托管指针 `pRemotingHost`。此指针可以执行以下 C# 代码:

   public class RemoteObjectLoader
     {
      public void Load()
       {
         ChannelServices.RegisterChannel(new TCPChannel(10002));
     RemotingConfiguration.RegisterWellKnownServiceType(
            typeof (JQD.MyClass),
        "MyClassURI", WellKnownObjectMode.SingleCall);
       }
     }

这完成了使用服务器 GC 启动 TCP 远程处理。如以下图所示,此过程利用托管和非托管代码来构建 CLR 宿主,并最终加载远程宿主代码:

Diagram for CLR Host

最后,为了说明起见,我在 `Load()` 中硬编码了远程类。在实际应用程序中,您将使用 `RemotingConfiguration.Configure` 来公开您的对象,并且我在示例下载中也是这样做的。

如何运行示例代码

下载 zip 文件后,将其解压缩到目录,例如 *c:\working\CLRMHost*。您需要执行以下几个步骤:

  • 打开解决方案(*clrmhost.sln*)并编译发布版本。
  • 将 *MyClass.dll* 从 *MyClass\Bin\Release* 复制到 *CLRMHost\release*。
  • 在命令提示符下,切换到 *CLRMHost\Release* 目录,然后键入“CLRMHost.exe -i”(这将安装服务)。
  • 如果您想卸载服务,请运行 *CLRMHost\uninstall.vbs*。
  • 打开 Services MMC 并启动名为“TCPRemotingHostServerGC”的服务。
  • 在 IE 浏览器中键入 https://:10000/MyClassTCPURI?wsdl,您应该会看到 WSDL 文档作为测试。然后,您可以编写一个针对端口 10003 的 TCP 远程客户端来测试 TCP 通道。

一些重要细节

此 .NET 解决方案主要包含两个项目:一个 C++.NET Windows 服务项目和一个远程加载器 .NET 类库项目。所有非托管 C++ 代码都位于 Windows 服务项目中,该项目已包含用于访问 `CorBindToRuntimeEx` 等的重要头文件 *mscoree.h*,以及用于访问 ATL 的 *atlbase.h*。此外,还导入了 *mscorlib.tlb* 和 *RemoteObjectLoader.tlb* 以实现从非托管进程到托管代码的转换。*RemoteObjectLoader.tlb* 可以通过命令“tlbexp”生成。请注意,有一个 bug,即 Windows 服务可执行文件的配置文件在编译时不会复制到发布或调试目录。因此,我利用 AppDomain 的 `BaseDirectory` 属性来设置远程配置文件路径。最后,由于此特定的 Windows 服务包含不可验证的 C++ 非托管代码,因此无法使用 *installUtil.exe* 来安装。幸运的是,C++ IDE 生成了用于安装的代码,如“CLRMHost.exe -i”。但是,要卸载,您必须运行包含的 VB 脚本文件 *uninstall.vbs*。此脚本使用 WMI 删除名为“TCPRemotingHostServerGC”的 Windows 服务。

结论

本文提供了一个使用 Windows 服务和服务器 GC 的 TCP 远程宿主的相当完整的实现。任何人都可以下载它并添加自己的对象来进行 TCP 远程处理。尽管它不像 IIS 托管的 HTTP 远程处理那样经过可伸缩性验证,但随着时间的推移,社区中的开发人员应该能够获得对 TCP 远程宿主的具体和真实的理解,并改进其可伸缩性和吞吐量。

© . All rights reserved.