2011-01-25 15 views
6

Tengo un proyecto bastante antiguo: cliente y servidor DCOM, ambos en C++ \ ATL, solo plataforma de Windows. Todo funciona bien: los clientes locales y remotos se conectan al servidor y trabajan simultáneamente sin ningún problema.DCOM: ¿Cómo cerrar la conexión en el servidor en el bloqueo del cliente?

Pero cuando los accidentes de cliente remoto o siendo asesinados por el Administrador de tareas o por mandato o poder "taskkill" apagar - Tengo un problema. Mi servidor no sabe nada sobre el bloqueo del cliente e intenta enviar nuevos eventos a todos los clientes (que también se han bloqueado). Como resultado, tengo una pausa (el servidor no puede enviar datos al cliente ya bloqueado) y su duración es proporcional a la cantidad de clientes remotos bloqueados. Después de 5 clientes bloqueados, las pausas son tan largas que es igual a la parada del servidor por completo.

que sé de DCOM mecanismo de "ping" (DCOM debe desconectar los clientes que no responde a "cada 2 minutos ping" después de 6 minutos de silencio). Y realmente, después de 6 minutos de suspensión, tengo un pequeño período de trabajo normal pero luego el servidor vuelve al estado "en pausa".

¿Qué puedo hacer con todo esto? ¿Cómo hacer que DCOM "ping" funcione bien? Si implementaré mi propio código "ping", ¿es posible desconectar manualmente la conexión de clientes DCOM? ¿Cómo hacerlo?

+0

¿Ha considerado enviar los eventos desde subprocesos de grupo de subprocesos para mitigar el bloqueo hasta cierto punto? – bdonlan

+0

@bdonlan: Esa podría ser una solución, sin embargo, esto complicará significativamente el servidor: tendrá que encargarse de esos hilos adicionales de por vida. – sharptooth

+0

No realmente; solo puede usar el grupo de subprocesos win32 incorporado. Si ya está usando un MTA, es bastante trivial pulsar QueueUserWorkItem. Si estás en una STA, deberías ordenar el control de la interfaz remota en el MTA, pero eso no es muy difícil de hacer con CoMarshalInterThreadInterfaceInStream, etc. – bdonlan

Respuesta

1

no estoy seguro sobre el sistema de ping DCOM, pero una opción para usted sería simplemente cultivar fuera de las notificaciones a un grupo de subprocesos por separado. Esto ayudará a mitigar el efecto de tener un pequeño número de clientes de bloqueo: empezarás a tener problemas cuando haya demasiados, por supuesto.

La forma más fácil de hacerlo es usar QueueUserWorkItem - esto invocará la devolución de llamada pasada en el grupo de subprocesos del sistema de la aplicación. Suponiendo que estés usando un MTA, esto es todo lo que necesita hacer:

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

Si el hilo principal se encuentra en una STA, esto es sólo un poco más compleja; sólo hay que utilizar CoMarshalInterThreadInterfaceInStream y CoGetInterfaceAndReleaseStream para pasar el puntero de interfaz entre apartamentos:

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

Tenga en cuenta que la comprobación de errores ha sido eludido por claridad - que, por supuesto que desee error de comprobación de todas las llamadas - en particular, que desea verificar RPC_S_SERVER_UNAVAILABLE y otros errores de red similares, y eliminar a los clientes infractores.

Algunas variaciones más sofisticadas que puede considerar incluyen asegurarse de que solo una solicitud está en vuelo por cliente a la vez (reduciendo aún más el impacto de un cliente atascado) y almacenar en caché el puntero de la interfaz ordenada en el MTA (si hilo principal es una STA) - ya que creo CoMarshalInterThreadInterfaceInStream pueden realizar solicitudes de red, que le idealmente quiere cuidar de él antes de tiempo cuando se sabe el cliente está conectado, en lugar de correr el riesgo de bloqueo en el hilo principal.

0

Una solución sería eliminar eventos - hacer que los clientes solicitar al servidor si hay algo de interés.

+1

Realmente, es imposible. Tengo cientos de eventos y es muy importante notificar al cliente sobre el evento lo más rápido posible. Si el cliente solicitará al servidor nuevos eventos incluso cada 1 segundo, no es lo suficientemente rápido. Si el cliente le preguntará al servidor 100 veces por segundo, entonces 5 clientes colgarán completamente el servidor, la red y la CPU. – Ezh

+0

@Ezh: suena razonable, pero ¿realmente lo ha probado? ¿Qué tan rápido se ejecuta un millón de llamadas "no hacer nada"? – sharptooth

+1

Sí, lo he probado. La llamada DCOM remota no es "no hacer nada". Es la comunicación de red, la seguridad de Windows, el cálculo de referencias, etc. Algunos milisegundos más la carga de la red y la CPU. – Ezh

0

Use DCOM para establecer una notificación llamada canalización. La desconexión se maneja mejor con tuberías. El oyente responde (casi) instantáneamente a los mensajes. p. Servidor-> Cliente (¿cuál es el nombre de tu pipa?). Cliente-> El servidor responde con un nombre que incluye la máquina. El cliente crea named pipe y escucha. El servidor abre la tubería de inmediato o cuando sea necesario.

0

Puede implementar su propio mecanismo de ping para que sus clientes llamen al método de ping del servidor de vez en cuando. Ya tiene algún tipo de contenedor para sus clientes en el lado del servidor. En ese mapa marque a cada cliente con una marca de tiempo del último ping. Luego, verifique si el cliente está vivo antes de enviar eventos a ese cliente. Puede personalizar una estrategia de cuándo dejar de enviar eventos, tal vez en función del tiempo o la cantidad de llamadas perdidas o el tipo de evento u otros factores. Probablemente no tenga que preocuparse por eliminar clientes; eso puede esperar hasta que DCOM se dé cuenta de que un cliente en particular está muerto. Este esquema puede no eliminar el problema por completo, ya que un cliente puede morir justo antes de que un evento deba ser enviado, pero tendrá un control total sobre cuántos de esos clientes pueden existir al ajustar el período de ping. Cuanto menor sea este período, menos clientes muertos, aunque pague con tráfico.

Cuestiones relacionadas