AppDomain 间通信问题
我一直在用 C# 开发 Windows 服务。
该服务启动时会提供一组配置文件路径。对于每个文件,服务将使用该文件作为其 ConfigurationFile
并使用该文件的文件夹作为 ApplicationBase
来启动一个 AppDomain
。每个文件夹都会有一个设置为 PrivateBinPath
的“bin”文件夹。
这些文件夹中的“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 日期:2011年3月11日 时间: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] 处重新抛出异常:at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg、IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& 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
实现者的远程代理似乎已经与生成的域中的另一端失去了联系。
任何人都可以解释为什么会发生这种情况,或者为默认域提供更好的方法来要求生成的域以整齐的方式关闭?
I've been developing a Windows Service in C#.
A set of configuration file paths is supplied to this service when it starts. For each of these files the service will spin up an AppDomain
using the file as its ConfigurationFile
and the folder of this file as the ApplicationBase
. Each folder will have a "bin" folder that is set as PrivateBinPath
.
The "bin" folder in these folders contain a small assembly that is shared in common with the service, this assembly contains the interface IServiceHost
. Also the type name and assembly name of a class that implements the IServiceHost
interface is known.
The whole CreateServiceHost
method looks like this:-
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;
}
The IServiceHost
interface is incredibly complex:-
public interface IServiceHost
{
void Start();
void Stop();
}
The service OnStart contains something like this:-
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();
}
}
The OnStop
is equally straight-forward (for now to keep things simple the IServiceHost.Stop
are blocking calls).
protected override void OnStop()
{
foreach (IServiceHost host in serviceHosts)
{
host.Stop();
}
}
This all simple enough and it works fine when testing on development machines. However in QA I'm getting exceptions when it is stopped. When in development we spin things up only for a short period it all seems to work fine. However in QA the service is only stopped every 24 hours. In this case it consistently fails to stop correctly.
Here is an example of what ends up in the Event log:-
Event Type: Error Event
Source: Workspace Services Event
Category: None Event ID: 0
Date: 11/03/2011 Time: 08:00:00
User: N/A Computer: QA-IIS-01
Description: Failed to stop service.
System.Runtime.Remoting.RemotingException:
Object
'/50e76ee1_3f40_40a1_9311_1256a0375f7d/msjxeib0oy+s0sog1mkeikjd_2.rem'
has been disconnected or does not
exist at the server.Server stack trace: at
System.Runtime.Remoting.Channels.ChannelServices.CheckDisconnectedOrCreateWellKnownObject(IMessage
msg) at
System.Runtime.Remoting.Channels.ChannelServices.SyncDispatchMessage(IMessage
msg)Exception rethrown at [0]: at
System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage
reqMsg, IMessage retMsg) at
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&
msgData, Int32 type) at
MyOrg.Service.IServiceHost.Stop()
at
MyOrg.Workspace.Service.MyAppService.OnStop()
at
System.ServiceProcess.ServiceBase.DeferredStop()For more information, see Help and
Support Center at
http://go.microsoft.com/fwlink/events.asp.
Now for test purposes the actual IServiceHost
simply posts entries to the event log as a heart beat and entries indicating start up and stop and I'm only spinning up a single AppDomain.
It would seem that over time the remote proxy for the implementer of IServiceHost
in the main service default app domain has lost touch with its other end in the generated domain.
Can anyone explain why that is happening, or offer a better way for the default domain to ask the generated domains to shutdown in a tidy manner?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这里是黑暗中的一刀。远程对象的生命周期租约是否到期?查看 MarshalByRefObject.InitializeLifetimeService。要使对象持久化,只需覆盖并返回
null
。A stab in the dark here. Is the lifetime lease on the remote object expiring? Look into MarshalByRefObject.InitializeLifetimeService. To make the object persistent, just override and return
null
.