Comunicación entre procesos

Tengo dos aplicaciones: X e Y.
X es la aplicación principal y maneja una gran cantidad de archivos XML. Tiene una historia de más de 10 años y se han utilizado media docena de técnicas para almacenar, procesar y manejar estos archivos XML.
Y es una herramienta de depuración que estoy desarrollando, que puede procesar y mostrar archivos XML en una forma más legible para los humanos. Básicamente, solo tiene una colección de hojas de estilo que detectará el formato XML y si reconoce el formato, transformará el XML en HTML que se muestra en un componente TWebBrowser.

Problema:
Cuando Y está activo, quiero que X envíe cualquier XML que procesa a Y para fines de visualización. ¡Pero solo cuando Y está corriendo! Si Y no se está ejecutando, X simplemente no hará nada.
La detección de Y debe hacerse en cualquier momento y debe ser rápida. Consideré usar la comunicación TCP / IP pero el retraso causado por una Y faltante es demasiado largo. Especialmente dado que muchas veces XML se procesa a veces. Mismo problema con los pipes nombrados y soluciones similares basadas en red. Necesito determinar rápidamente si Y se está ejecutando y está disponible, y si es así, enviar el XML rápidamente y luego continuar con X.
También consideré hacer una aplicación basada en COM o tal vez agregar una DLL basada en COM con eventos que permitan la comunicación entre procesos. La solución DLL sería interesante ya que expondría un método a X para cargar un archivo XML, luego enviaría un evento a Y para procesar el XML. Esta parece ser la mejor opción aunque también necesitaría verificar si el DLL está registrado o no. Si no, ¡X ni siquiera puede llamarlo!
La aplicación X también será utilizada por clientes que no recibirán Y ni la DLL adicional, por lo que en la mayoría de los casos, la DLL no se registrará. (Como dije, está destinado a ayudar durante la depuración …)

Pero tal vez hay otras opciones? TCP / IP es demasiado lento, COM es un poco demasiado complejo.

X e Y se ejecutarán en el mismo sistema. O solo X estará en un sistema y Y falta por completo.


Acerca del uso de archivos mapeados en memoria … Aunque es práctico, debo tener en cuenta que la mayoría de las veces, Y no se ejecutará, por lo tanto, el archivo MMF perderá memoria. Los datos XML pueden tener hasta 4 MB de tamaño dentro de X, por lo que tener varios bloques de este tamaño en memoria es un poco exagerado. Se puede usar para enviar mensajes de estado entre X e Y, pero la memoria a veces es un problema con la aplicación X. Y mientras un archivo MMF se puede conectar a un archivo físico, bash evitar escribir archivos temporales por completo.
Es una buena solución, pero temo que no es lo suficientemente buena.


Algunas explicaciones adicionales están en orden, creo. Application X es una aplicación que se utilizará durante varias horas, con usuarios que realizan muchas acciones que se traducen en gran cantidad de datos XML que se procesan. Application X es una aplicación de escritorio que se comunica con varias aplicaciones web (REST), servicios web (SOAP) y otras aplicaciones, y la mayoría de esto se hace a través de XML.
La aplicación Y solo pretende echar un vistazo dentro de los procesos que X está ejecutando. Básicamente, X está funcionando durante 20 minutos y Y aparece. A partir de ese momento, X debería comenzar a enviar XML a Y hasta que Y desaparezca nuevamente o hasta que X finalice. En la mayoría de los casos, Y solo se ejecutará para capturar solo una pequeña parte de las tareas en ejecución e incluso posiblemente se inicie varias veces. Pero podría estar pensando en todo en la dirección equivocada. Quizás X debería ser un servidor con Y registrándose … No es un verdadero problema cuando Y no puede encontrar X. Pero X no encuentra Y no puede dar lugar a retrasos u otros problemas …

Puede hacerlo de manera más sencilla, ya que solo está tratando de averiguar si una aplicación se está ejecutando desde otra. Siempre y cuando se ejecuten en la misma máquina por el mismo usuario, puede hacer que X simplemente use FindWindow () para ver si Y se está ejecutando actualmente. Solo asegúrese de darle a Y un nombre significativo (en el ejemplo a continuación, es TXMLFormatterForm):

 var XMLWindow: HWnd; begin XMLWindow := FindWindow('TXMLFormatterForm', nil); if XMLWindow > 0 then // Y is running end; 

También puede usar el título de la ventana de Y (título), siempre y cuando esté seguro de que es distinto:

 XMLWindow := FindWindow(nil, 'Workshop Alex's XML Formatter'); 

Mire mi IPC en:

http://www.cromis.net/blog/downloads/cromis-ipc/

Es rápido, gratuito y tiene un tiempo de espera ajustable, por lo que puede establecerlo en una cantidad muy pequeña (50 ms, por ejemplo). Debido a que es muy rápido (la típica solicitud de ciclo de mensaje -> proceso -> la respuesta tarda menos de 1 ms, alrededor de 0.1 ms) puede tener tiempos de espera muy pequeños. Tiene un servidor cliente incorporado, por lo que muchos clientes no son un problema. Se ejecuta con el grupo de tareas detrás, por lo que no bloquea el progtwig y tiene paquetes de datos muy flexibles para facilitar la escritura / lectura de los datos.

Como ya se dijo, puede verificar con otros medios si el depurador se está ejecutando.

  • Verifica el proceso
  • Compruebe la ventana principal del proceso
  • Use un Mutex

Puede hacer que X escriba su salida en un archivo mapeado en memoria : Y puede recuperar los datos si se está ejecutando. De esta manera a X no le importa si Y está arriba.

X podría escribir algún tipo de información de control en una ubicación conocida (por ejemplo, almacenar las compensaciones de los últimos 1000 XML escritos comenzando en el desplazamiento 0 en el archivo asignado) y usar el rest del archivo como un buffer circular para los datos brutos.

Si necesita que Y sea el factor determinante para las acciones en X, haga Y cree el archivo asignado y luego use su presencia / ausencia como verificación de la producción de datos en el lado X del “canal”. Aquí hay un código de ejemplo para el creador y el segundo usuario.

Los conductos con nombre son rápidos, porque están basados ​​en archivos mapeados en memoria, para su implementación. Lo que podría ser lento es el tiempo de espera en caso de falla del servidor …

Si necesita una respuesta rápida en la misma computadora, ¿por qué no usa buenos viejos mensajes de GDI?

Puede usar estos mensajes, incluso sin Interfaz de usuario o forma visual, en aplicaciones simples de consola o servicio en segundo plano (en el caso de una aplicación de servicio, la configuración de seguridad debe especificar que este servicio debe interactuar con el escritorio, es decir, debe poder para recibir y enviar mensajes).

El truco es manejar mensajes WM_COPYDATA. Consulte, por ejemplo, nuestra clase TSQLRestClientURIMessage y los métodos ExportServerMessage / AnswerToMessage de TSQLRestServer, tal como se implementa en http://synopse.info/fossil/finfo?name=SQLite3/SQLite3Commons.pas.

En la práctica, descubrí que los mensajes GDI son mucho más rápidos que los named pipes para pequeñas cantidades de datos (hasta 64 KB o más por mensaje).

Tiene alternativas en Buscar una alternativa a los mensajes de Windows utilizados en la comunicación entre procesos

Aquí hay algunos datos reales sobre la velocidad, de acuerdo con diversas investigaciones de clientes / servidores.

Todos los puntos de referencia se ejecutaron localmente en una computadora. Puede lograr más de 15000 consultas por segundo en acceso directo, 4300 consultas por segundo en acceso remoto HTTP / 1.1. Esto se midió en un Notebook que usaba una CPU Centrino2, con AntiVirus ON.

 2.5. Client server access: - Http client keep alive: 3001 assertions passed first in 7.87ms, done in 153.37ms ie 6520/s, average 153us - Http client multi connect: 3001 assertions passed first in 151us, done in 305.98ms ie 3268/s, average 305us - Named pipe access: 3003 assertions passed first in 78.67ms, done in 187.15ms ie 5343/s, average 187us - Local window messages: 3002 assertions passed first in 148us, done in 112.90ms ie 8857/s, average 112us - Direct in process access: 3001 assertions passed first in 44us, done in 41.69ms ie 23981/s, average 41us Total failed: 0 / 15014 - Client server access PASSED 

Este benchmark prueba la velocidad del cliente y del servidor, y no es multiproceso (incluso si nuestro framework es seguro para múltiples hilos).

Así que puedes adivinar eso, para un bloque de datos de 4 KB de contenido JSON para cada solicitud:

  1. El acceso directo (como su enfoque dll) es el más rápido y consume menos recursos.
  2. Luego, los mensajes de GDI.
  3. Luego, pipas nombradas.
  4. Luego FastCGI y HTTP (dependiendo de su servidor web para FastCGI, las clases HTTP consumen muy poco).

Las conexiones Keep alive son conexiones HTTP / 1.1, y multi connect es simple HTTP / 1.0, con una nueva conexión para cada solicitud. La conexión múltiple no es tan mala, porque desde el punto de vista del servidor, utilizamos un grupo de subprocesos eficiente basado en puertos de finalización de E / S.

Ver http://synopse.info/forum/viewtopic.php?id=90

Usa archivos mapeados en memoria. Son geniales para tus tareas particulares. Y nuestro MsgConnect con su transporte MMF vendrá a rescatar si no tiene tiempo o intención de implementar un esquema personalizado.

Me gustaría ir a la dirección de archivos asignados de memoria, pero no implementaría directamente en la ruta del código … en lugar de eso iría a través de un objeto intermedio que realizaría la escritura en el archivo mapeado en la memoria, de esta manera podría reemplazarse por uno que simplemente descartó los datos si no estaban en su lugar.

Cuando el progtwig comienza por primera vez (o se le dice que verifique mediante un cambio de configuración), el sistema crearía el objeto “no hacer nada” o el “registro a través de archivos asignados a la memoria”. Esto también le brinda la capacidad de agregar un depurador posterior cuando surja la necesidad … como un registrador UDP para el registro de red, etc.

Puede usar la llamada “FindWindow” para ver si su depurador se está ejecutando.

Si está dispuesto a usar archivos temporales como transporte, será fácil

  1. Ambos progtwigs registran un mismo mensaje personalizado al iniciar MsgAppNotifyID: = RegisterWindowMessage (……);

  2. El progtwig Y debe tener códigos para manejar el mensaje personalizado usando SetWindowLong o cualquier cosa equivalente

  3. Progtwig X, usando FindWindow para ver si el progtwig Y se está ejecutando o no

  4. Progtwig X, si Y se está ejecutando, cree un archivo temporal en una ubicación / directorio conocido de ambos extremos utilizando un nombre de archivo consistente como el formato XXYY1234 (XXYYn) donde 1234 = n

  5. Progtwig X, use BroadcastSystemMessage para señalizar el progtwig Y NotifyRecipients: = BSM_APPLICATIONS; BroadcastSystemMessage (BSF_IGNORECURRENTTASK o BSF_POSTMESSAGE o BSF_FORCEIFHUNG, @NotifyRecipients, MsgAppNotifyID, cualquier valor personalizado, n);

  6. Progtwig y, procesa el mensaje anterior usando WParam como n y reconstruye el archivo para el proceso

Saludos Buena suerte

ejecutar un servidor TCP que solo en una conexión (como un disparador) es realmente rápido y debe hacer lo que quiera