跨AppDomain通信问题

Posted

技术标签:

【中文标题】跨AppDomain通信问题【英文标题】:Inter-AppDomain communication problem 【发布时间】:2011-07-13 15:33:07 【问题描述】:

我一直在用 C# 开发 Windows 服务。

此服务启动时会提供一组配置文件路径。对于这些文件中的每一个,该服务将使用该文件作为其ConfigurationFile 并将该文件的文件夹作为ApplicationBase 来启动一个AppDomain。每个文件夹都有一个“bin”文件夹,设置为PrivateBinPath

这些文件夹中的“bin”文件夹包含一个与服务共享的小程序集,该程序集包含接口IServiceHost。实现IServiceHost 接口的类的类型名称和程序集名称也是已知的。

整个CreateServiceHost 方法如下所示:-

    public static IServiceHost CreateServiceHost(string configPath, string entryAssembly, string entryType)
    
        IServiceHost host;

        AppDomainSetup setupInfo = new AppDomainSetup();
        setupInfo.ApplicationBase = Path.GetDirectoryName(configPath);
        setupInfo.PrivateBinPath = Path.Combine(setupInfo.ApplicationBase, "bin");
        setupInfo.ShadowCopyFiles = "true";
        setupInfo.ConfigurationFile = configPath;

        AppDomain appDomain = AppDomain.CreateDomain("Service for: " + setupInfo.ApplicationBase, AppDomain.CurrentDomain.Evidence, setupInfo);


        object objHost = appDomain.CreateInstanceFromAndUnwrap(Path.Combine(setupInfo.PrivateBinPath, entryAssembly), entryType);
        host = (IServiceHost)objHost;

        return host;
    

IServiceHost 接口非常复杂:-

public interface IServiceHost

    void Start();
    void Stop();

服务 OnStart 包含如下内容:-

private List<IServiceHost> serviceHosts = new List<IServiceHost>();

protected override void OnStart(string[] args)

    foreach (string configPaths in GetConfigPaths())
    
        IServiceHost host = ServiceHostLoader.CreateServiceHost(configPath);
        serviceHosts.Add(host);
        host.Start();
    

OnStop 同样直截了当(为了简单起见,IServiceHost.Stop 正在阻塞调用)。

protected override void OnStop()

    foreach (IServiceHost host in serviceHosts)
    
        host.Stop();
    

这一切都很简单,在开发机器上进行测试时效果很好。但是在质量检查中,当它停止时我会遇到异常。在开发过程中,我们只在很短的时间内启动它,这一切似乎都运行良好。但是在 QA 中,该服务仅每 24 小时停止一次。在这种情况下,它始终无法正确停止。

以下是事件日志中最终结果的示例:-

事件类型:错误事件 资料来源:工作区服务活动 类别:无事件 ID:0 日期:11/03/2011 时间:08:00:00 用户:N/A 计算机:QA-IIS-01 描述:无法停止服务。 System.Runtime.Remoting.RemotingException: 目的 '/50e76ee1_3f40_40a1_9311_1256a0375f7d/msjxeib0oy+s0sog1mkeikjd_2.rem' 已断开或未断开 存在于服务器上。

服务器堆栈跟踪:在 System.Runtime.Remoting.Channels.ChannelServices.CheckDisconnectedOrCreateWellKnownObject(IMessage 味精)在 System.Runtime.Remoting.Channels.ChannelServices.SyncDispatchMessage(IMessage 味精)

在 [0] 处重新抛出异常:在 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg,IMessage retMsg)在 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(消息数据& msgData,Int32 类型)在 MyOrg.Service.IServiceHost.Stop() 在 MyOrg.Workspace.Service.MyAppService.OnStop() 在 System.ServiceProcess.ServiceBase.DeferredStop()

有关详细信息,请参阅帮助和 支持中心 http://go.microsoft.com/fwlink/events.asp.

现在出于测试目的,实际的 IServiceHost 只是将条目作为心跳和指示启动和停止的条目发布到事件日志中,而我只启动一个 AppDomain。

随着时间的推移,主服务默认应用程序域中IServiceHost 的实现者的远程代理似乎与生成域中的另一端失去了联系。

谁能解释为什么会发生这种情况,或者为默认域提供更好的方法来要求生成的域以整洁的方式关闭?

【问题讨论】:

开源项目 Topshelf 有这个功能,叫做 Shelving,它包括崩溃恢复和监控等功能。您在上面发布的服务代码看起来几乎与每个 AppDomain 的设置方式完全相同。 请阅读我关于跨AppDomain通信的博客blog.vcillusion.co.in/… 【参考方案1】:

这里是黑暗中的刺。远程对象的终身租约是否到期?查看MarshalByRefObject.InitializeLifetimeService。要使对象持久化,只需覆盖并返回null

public override object InitializeLifetimeService()

    // returning null here will prevent the lease manager
    // from deleting the object.
    return null;

【讨论】:

同意,默认为 10 分钟。刚好足够长,不会在调试器中捕捉到这一点。 @Hans:是的,如果我让我的调试版本运行 15 分钟然后停止服务,我会遇到我在 QA 中看到的相同错误。现在只是测试修复。 非常感谢它确实解决了这个问题。很高兴能回答 *** 问题。我以后应该问更多问题。

以上是关于跨AppDomain通信问题的主要内容,如果未能解决你的问题,请参考以下文章

AppDomain 间通信和接口

使用 JointCode.Shuttle 访问任意 AppDomain 的服务

JointCode.Shuttle,一个简单高效的跨 AppDomain 通信的服务框架

AppDomain 通信和性能

AppDomains之间的通信

AppDomains之间如何最好地进行通信?