¿Cuál es la forma preferida de que el código del servidor notifique a los clientes múltiples sobre los cambios en los datos dentro de una sola aplicación Delphi?
notifications (2)
A usó la API de Windows para resolver un problema similar a este. Pero creo que mi enfoque es más simple. En mi solicitud, el evento no conocía la cantidad de "clientes" y qué formularios eran realmente clientes.
Lo que hice fue:
Transmita un mensaje de Windows a todos los formularios abiertos por mi aplicación (
screen.forms[X]
). El mensaje incluye en el WParam un puntero a un registro que contiene información sobre un evento. Además, diferentes acciones transmiten mensajes distinc.WM_RECORDUPDATE
para actualizaciones deWM_RECORDUPDATE
de datos, por ejemplo.Windows (clientes) escuchan mensajes en el camino de:
procedure RecordUpdateMessage(var msg: TMessage); message WM_RECORDUPDATE;
Procedimiento RecordUpdateMessage
lee el registro apuntado por msg.WParam y en función de los datos en el registro, si desea reaccionar a la actualización, insertar o eliminar un registro en el DB.
Tengo una aplicación Delphi grande que tiene un código central de ''servidor'' que contiene mis datos. Dentro de la misma aplicación, ''cliente'', el usuario puede abrir y cerrar múltiples formularios ''modales'' no modales para inspeccionar estos datos. Los cambios en los datos se dividen en dos tipos: principales (por ejemplo, cambios estructurales, como datos que se han agregado o eliminado) y menores, como un cambio en el valor de los datos. Los formularios de clientes abiertos existentes deben actualizarse para mostrar los datos modificados en un tiempo breve. Esta no es una base de datos, mi ''servidor'' usa mis propias estructuras de datos, por lo que es posible que mis soluciones hayan pasado por alto técnicas posiblemente estándares que están disponibles dentro de una estructura de base de datos formal. Dicho esto, he repetido mis soluciones tantas veces que pensé que podría preguntar si hay técnicas formales y posiblemente componentes de Delphi que mejorarían o simplificarían mi código. Estoy a punto de pasar al código multiproceso que hace que la pregunta sea aún más relevante para mí.
Yo uso dos métodos:
Marca de tiempo El código ''servidor'' mantiene un valor Int64 tomado de QueryPerformanceCounter. Los formularios del cliente examinan este valor en un temporizador de 300 ms y se actualizan si su copia de la marca de tiempo difiere de la del servidor. Supongo que esta es mi solución ''pull''.
Notificación de interfaz. El código ''servidor'' mantiene una clase descendiente de TInterfaceList con los métodos AddClient y RemoveClient que registran una interfaz simple de notificación de cliente común. Cada uno de los clientes se registra con esta lista cuando se crea y anula el registro en destroy. Los cambios de datos en el servidor activan una iteración a través de esta lista llamando a cada cliente para avisarle sobre el cambio. Supongo que esta es mi solución ''push''.
Me encantan las interfaces y la solución 2 parece agradable, ya que evita los tic-tac temporizadores y se depura fácilmente (aunque las llamadas anular el registro pueden ser problemáticas con el fin de la destrucción). También hay posibles implicaciones de rendimiento porque es muy probable que haya miles de cambios de datos por segundo y tengo que tener cuidado de usar un mecanismo BeginUpdate / EndUpdate para convertir mis muchos cambios de datos del servidor en una sola llamada de notificación. Finalmente, termino necesitando un temporizador de algún tipo para agregar las llamadas en una actualización suave de una forma mostrada.
Ambas soluciones funcionan bien y estoy dividido entre los dos. Para una solución multiproceso, estoy seguro de que hay otras trampas de las que no sé nada. Cualquier comentario será bienvenido. Estoy usando XE2.
Debe tener en cuenta lo que desea que suceda cuando crezca el número de clientes, y luego decidir entre los dos males:
- ¿Está bien si mi rendimiento se degrada mientras estoy seguro de que todos los datos están actualizados, siempre y en todas partes en la aplicación (entonces se necesita el patrón del observador)?
- ¿Está bien si los datos en algunos lugares se retrasan para mejorar el rendimiento (entonces podrías usar el sondeo y hacer que el intervalo sea más prolongado cuando las repeticiones del sondeo causan demasiada ralentización)?
No soy partidario de las encuestas, ya que generalmente conduce a soluciones muy intrincadas (bueno, al menos las cosas que probé, tal vez lo hice de la manera incorrecta en aquel entonces).
Implementaría el patrón Observer en Delphi usando interfaces, podría usar this o this como un comienzo.