2011-03-11 20 views
13

He estado desarrollando un servicio de Windows en C#.Problema de comunicación Inter-AppDomain

Un conjunto de rutas de archivos de configuración se suministra a este servicio cuando se inicia. Para cada uno de estos archivos, el servicio girará un AppDomain usando el archivo como su ConfigurationFile y la carpeta de este archivo como ApplicationBase. Cada carpeta tendrá una carpeta "bin" configurada como PrivateBinPath.

La carpeta "bin" en estas carpetas contiene un pequeño conjunto que se comparte en común con el servicio, este conjunto contiene la interfaz IServiceHost. También se conoce el nombre del tipo y el nombre del ensamblado de una clase que implementa la interfaz IServiceHost.

El conjunto CreateServiceHost método se parece a esto: -

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; 
    } 

La interfaz IServiceHost es increíblemente compleja: -

public interface IServiceHost 
{ 
    void Start(); 
    void Stop(); 
} 

El servicio OnStart contiene algo como esto: -

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(); 
    } 
} 

El OnStop es igualmente directo (para ahora para mantener las cosas simples, IServiceHost.Stop están bloqueando llamadas).

protected override void OnStop() 
{ 
    foreach (IServiceHost host in serviceHosts) 
    { 
     host.Stop(); 
    } 
} 

Todo esto es lo suficientemente simple y funciona bien cuando se prueba en máquinas de desarrollo. Sin embargo, en QA obtengo excepciones cuando se detiene. Cuando estamos en desarrollo, hacemos girar las cosas solo durante un corto período, todo parece funcionar bien. Sin embargo, en QA, el servicio solo se detiene cada 24 horas. En este caso, no puede detenerse correctamente.

Aquí es un ejemplo de lo que termina en el registro de eventos: -

Tipo de evento: Error Evento Fuente: Servicios de área de trabajo Evento Categoría: Ninguno Identificación del acontecimiento: 0 Fecha: 11/03/2011 Hora: 08:00:00 Usuario: N/A Ordenador: QA-IIS-01 Descripción: Error al detener el servicio. System.Runtime.Remoting.RemotingException: objeto '/50e76ee1_3f40_40a1_9311_1256a0375f7d/msjxeib0oy+s0sog1mkeikjd_2.rem' se ha desconectado o no existir en el servidor. relanza en System.Runtime.Remoting.Channels.ChannelServices.CheckDisconnectedOrCreateWellKnownObject (I-Mensaje msg) en System.Runtime.Remoting.Channels.ChannelServices.SyncDispatchMessage (I-Mensaje msg)

Excepción:

servidor de seguimiento de la pila en [0]: en System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage (I-Mensaje reqMsg, I-Mensaje retMsg) en System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke (MessageData & MSGDATA, tipo Int32) en MyOrg.Service.IServiceHost.Stop() en MyOrg.Workspace.Service.MyAppService.OnStop() en System.ServiceProcess.ServiceBase.DeferredStop()

Para obtener más información, vea la Ayuda y Support Center en http://go.microsoft.com/fwlink/events.asp.

Ahora con fines de prueba de la actual IServiceHost simplemente envía entradas al registro de eventos como un latido del corazón y las entradas que indica el inicio y paro y yo sólo estoy girando una sola dominio de aplicación.

Parece que con el tiempo el proxy remoto para el implementador de IServiceHost en el dominio de la aplicación predeterminada de servicio principal ha perdido contacto con su otro extremo en el dominio generado.

¿Alguien puede explicar por qué está sucediendo esto u ofrecer una forma mejor para que el dominio predeterminado pida a los dominios generados que cierren de manera ordenada?

+0

El proyecto de código abierto Topshelf tiene esta característica, se llama Estanterías, e incluye cosas como la recuperación de fallos y la supervisión. El código de servicio que publicó anteriormente se ve casi exactamente como la configuración de cada AppDomain. –

Respuesta

23

Una puñalada en la oscuridad aquí. ¿El contrato de por vida del objeto remoto expira? Mire en MarshalByRefObject.InitializeLifetimeService. Para que el objeto sea persistente, solo anule y devuelva null.

public override object InitializeLifetimeService() 
{ 
    // returning null here will prevent the lease manager 
    // from deleting the object. 
    return null; 
} 
+0

De acuerdo, 10 minutos es el valor predeterminado. Casi exactamente el tiempo suficiente para no detectar esto en el depurador. –

+0

Sonidos prometedores @Hans: Probando el código existente ahora ... – AnthonyWJones

+0

@Hans: Sí, si dejo mi versión de depuración en funcionamiento durante 15 minutos y luego dejo de recibir el servicio, obtengo el mismo error que veo en QA. Estoy probando la solución ahora. – AnthonyWJones