none
Un servicio WCF para controlar un proceso en Background RRS feed

  • Pregunta

  • Buenos días para toda la comunidad.

    Estoy diseñando una aplicación "Procesador" que debe realizar cómputos en background y debe recibir señales para iniciar, pausar, reiniciar o terminar desde el mismo host donde se ejecuta y también desde otros clientes "Controladores". También se necesita que varias instancias de la aplicación Procesador puedan ejecutarse en diferentes hosts (una instancia en cada host) y que los controladores puedan controlar cualquiera de las instancias.

    Por motivos que no vale la pena comentar, la aplicación no puede ser un servicio Windows y no puede ejecutarse en un IIS. En otras palabras, tiene que ser una aplicación de consola.

    Mi primera idea fue utilizar la misma base de datos donde se encuentran los datos que hay que usar en los cómputos para establecer un mecanismo de control y que los controladores escribieran en esa base de datos. 

    Otra opción que estoy analizando es usar WCF para recibir las "señales" desde los controladores.

    He encontrado muchos ejemplos de servicos WCF auto-hospedados en aplicaciones de consola, pero todos siguen un modelo de solicitud-procesamiento-respuesta, mientras que lo que yo necesito es un modelo de procesamiento en background con control asíncrono.

    Lo que me confunde un poco es el modelo de threading de WCF. Supongamos que creo una clase ProcessorService que implementa un ServiceContract para las operaciones Start, Pause, Restart y Terminate. Mi aplicación de consola crea un ServiceHost para ProcessorService y hace un Thread.Sleep. ¿Las operaciones Start, Pause, Restar, etc... se reciben en el mismo thread de la aplicación principal, o en otro? Yo supongo que se reciben de forma asíncrona en otro thread (¿del pool de threads de IO?), por lo que al recibir la señal de Start, ese thread tendría que comunicarse (por ejemplo, mediante eventos) con el Thread principal para que este iniciara el procesamiento. Luego, si se recibiera una operación de Pause, tendría que notificar al thread principal para que detuviera el procesamiento y así sucesivamete para todas las operaciones.

    Ahora las preguntas:

    • ¿Alguien me puede referir un ejemplo de una implementación similar a lo que estoy intentando hacer?
    • ¿Podrían comentarme si mi enfoque es adecuado, o si existe una mejor forma de hacerlo?

    ¡Muchas gracias!


    logo osoft
    Si he contestado tu pregunta, por favor marca mi post como respuesta.
    ...Y si mi post te ha servido, márcalo como útil 


    domingo, 4 de enero de 2015 10:35

Respuestas

  • >>tendré que usar alguna forma de hilos pero aún no he llegado hasta el punto de decidir cómo hacerlo

    pero para que quieres usar hilo si WCF ya lo implementa

    si analizaste el link que puse veras que al nostrar un servicio puedes indicar s el servicio atiende multiples instancias, con esto ya no necesitas que tu aplicacion implemente de forma explicita threads

    El modelo que intento implementar es instanciar el ServiceHost, hacer Open, entrar en un ciclo que, dependiendo de un estado, espera sin hacer nada (antes del Start o después de un Pause), procesa algunos datos (luego del Start o Restart) o sale del ciclo (luego del Terminate), cierra la comunicación y termina.

    no entendi

    como es eso de espera sin hacer nada antes del start ? sino iniciaste es logico que no haga nada

    no veo donde interviene WCF ? no veo que menciones en ningun lago la interaccion de un cliente que invoca la funcionalidad del servicio?

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    domingo, 4 de enero de 2015 20:42
  • Buenos días,

    según lo que planteas, creo que una posible solución sería crear un servicio auto hosteado con una máquina de estados (Workflow State Machine).

    http://channel9.msdn.com/Shows/Workflow-TV/endpointtv-Getting-Started-With-WF4-State-Machine

    http://workflowdemo.codeplex.com/

    Hay bastante documentación sobre cómo implementar máquinas de estados con Workflow Foundation. Yo no descartaría investigar lo que te propongo.



    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó vótala como útil. Saludos

    lunes, 5 de enero de 2015 9:36
    Moderador

Todas las respuestas

  • Perdona, pero yo no veo claro como se va a arrancar esa aplicación de consola para que luego pueda atender las peticiones de arraque, parada y pausa de los clientes.

    Por otra parte no veo que la aplicación tenga que llamar a Thead.Sleep para nada. Cuando un cliente solicita empezar a hacer una determinada tarea, lo que tendría que hacer la aplicación es arrancar una tarea System.Threading.Task con un CancelationToken para que esta tarea se pueda interrumpir. La tarea que realiza la operación tiene que comprobar periódicamente que no se ha cancelado la tarea inspeccionando el CancelationToken.



    Jesús López


    EntityLite a lightweight, database first, micro orm

    domingo, 4 de enero de 2015 12:19
  • >>Mi aplicación de consola crea un ServiceHost para ProcessorService y hace un Thread.Sleep. ¿Las operaciones Start, Pause, Restar, etc... se reciben en el mismo thread de la aplicación principal, o en otro?

    creo que deberias analizar lo comentado aqui

    WCF Concurrency (Single, Multiple, and Reentrant) and Throttling

    ¿Cómo quiero que se comporten las instancias de mi servicio WCF?

    alli se plantea esto de lo cual tienes dudas

    ----

    hasta donde entiendo la aplicaicon de consola tiene su thread, y el servicio usa otro, pero si aplcias un sleep vas a detener el del servicio no el de la aplicacion de consola, ojo puede que me este equivocando, pero entiendo que el servicio usa un thread separado para escuchar en el puerto que indiques al servicio, el tema es configurar al servicio como levanta las instancias

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina


    domingo, 4 de enero de 2015 13:37
  • Jesús: no importa cómo se arranque la aplicación. Supongamos que alguien la ejecuta, o que la inicia el task manager de windows o que la inicia un producto de scheduling de terceros como Control-M.

    En cuanto a lo del Thread.Sleep: posiblemente tengas razón en que no sea necesario. Ahora bien, ningún cliente solicita hacer una tarea. El cliente lo único que puede hacer es iniciar el procesamiento, pausarlo, reiniciarlo y terminarlo. La aplicación Procesador sabrá qué tiene que hacer, no es el cliente quien tiene que indicárselo. Una vez que la aplicación procesador recibe la señal de Start, comienza a procesar datos de forma continua y sin detenerse hasta que reciba una señal Pause o Terminate. Si recibe una señal Pause, se queda esperando hasta recibir una señal Restar o una señal Terminate.

    No estoy seguro de querer usar Tasks, todavía tengo que pensarlo un poco. Posiblemente un cancellation token no es la mejor aproximación a la solución. 

    Gracias por tu contribución.


    logo osoft
    Si he contestado tu pregunta, por favor marca mi post como respuesta.
    ...Y si mi post te ha servido, márcalo como útil smile

    domingo, 4 de enero de 2015 13:49
  • Hola Leandro, ¡Tanto tiempo! Es un gusto volver a saludarte.

    Gracias. Ya había revisado el primer link que comentas. Tengo la idea de que mi servicio debe implementarse con InstanceContextMode.Single y ConcurrencyModel.Single. La parte que atañe a WCF es básicamente un Singleton, no necesito más que una instancia y además necesito que todas las solicitudes al servicio se atiendan de forma secuencial.

    Voy a leer el otro enlance.

    ¡Muchas gracias!


    logo osoft
    Si he contestado tu pregunta, por favor marca mi post como respuesta.
    ...Y si mi post te ha servido, márcalo como útil smile

    domingo, 4 de enero de 2015 14:00
  • >> Jesús: no importa cómo se arranque la aplicación. Supongamos que alguien la ejecuta, o que la inicia el task manager de windows o que la inicia un producto de scheduling de terceros como Control-M.

    <<

    Yo creo que sí que importa, si un cliente solicita que se inicie el procesamiento y la aplicación no está corriendo, no podrá iniciar el procesamiento. ¿o sí?

    Con tareas yo me refería a iniciar, pausar, reiniciar o lo que sea. En cualquier caso cuando por ejemplo reciba una petición de iniciar, la aplicación deberá ponerse a hacer el trabajo en otro hilo y devolverle al cliente un OK diciendo que ha empezado. Porque si lo hace en el mismo hilo en el que recibe la petición, el cliente se quedaría congelado (blocked) hasta que terminara de hacer el trabajo. O sea, tendrás que usar hilos de cualquier manera. ¿Cómo entonces vas a interrumpir un hilo, sea para pausarlo, cancelarlo o reiniciarlo? Thread.Abort es una de las peores prácticas que se pueden utilizar con hilos, mientras que con System.Threading.Task existe una forma "suave" de interrumpir un hilo mediante CancelationToken.



    Jesús López


    EntityLite a lightweight, database first, micro orm

    domingo, 4 de enero de 2015 18:41
  • Vale, vale, sí importa pero no importa :) Lo que quiero decir es que da igual si la aplicación la arranca el task scheduler de windows o el usuario. Si la aplicación está ejecutando, atenderá las solicitudes. Si no está ejecutando, el cliente ni siquiera podrá conectarse.

    Efectivamente, tendré que usar alguna forma de hilos pero aún no he llegado hasta el punto de decidir cómo hacerlo. Lo de usar Thread.Abort no se me había ocurrido, es una de las cosas que evito (la otra cosa que evito es pisar serpientes venenosas).

    Ahora mismo ni siquiera he pensado cuánto paralelismo quiero darle al Procesador. Una primera idea es que el hilo que procesa vaya cogiendo "bocados" de datos y procesándolos. Al terminar un "bocado" comienza con el siguiente. Algunos "bocados" son completamente independientes de otros, así que podría tener más de un hilo "masticando" de forma simultánea. Naturalmente, si el procesador recibe la orden de pausar, todos los hilos deben parar. Lo que planteas en cuanto al uso de tasks me parece bien, pero todavía no quiero tomar decisiones en cuanto a paralelismo sin antes tener claro el modelo de control.

    Si miramos los ejemplos de aplicaciones wfc self-hosted que hay por allí, todas instancian el ServiceHost, hacen Open, esperan una condición de parada, hacen Close y terminan. 

    El modelo que intento implementar es instanciar el ServiceHost, hacer Open, entrar en un ciclo que, dependiendo de un estado, espera sin hacer nada (antes del Start o después de un Pause), procesa algunos datos (luego del Start o Restart) o sale del ciclo (luego del Terminate), cierra la comunicación y termina.

    Por otro lado, estoy intentando que la lógica del procesamiento esté fuera de ese ciclo. Por eso mi idea inicial era que la aplicación Procesador, luego de inicializar todo lo que tenga que inicializar, cree una instancia de la clase que realmente hace el trabajo y llame asíncronamente a un método de esa clase que es el que procesa los datos que vaya encontrando. Entonces lo único que tiene que hacer el Procesador, después de llamar asíncronamente al método es un Wait, pero no le veo mucho sentido a hacer una llamada asíncrona seguida de un Wait... para eso hago la llamada de forma síncrona.

    Ahora bien, ese modelo tiene algunos problemas que habría que solucionar, como por ejemplo la actualización de objetos desde un hilo distinto a donde se crearon. Por eso os planteé la pregunta con la esperanza de que alguien hubiera enfrentado un escenario similiar y no tener que reinventar la rueda.

    Gracias de nuevo por vuestras respuestas.


    logo osoft
    Si he contestado tu pregunta, por favor marca mi post como respuesta.
    ...Y si mi post te ha servido, márcalo como útil smile

    domingo, 4 de enero de 2015 19:19
  • Pues yo creo que la parte más fácil es la de control. Exponer un servicio WCF es la parte trivial del problema. Lo realmente complicado es tener un proceso que lo puedas arrancar, pausar, reanudar y reiniciar. 

    Yo lo haría teniendo un patrón singleton. Una clase MyProcess con una propiedad estática Current.

    El Servicio WCF puede ser perfectamente un servicio WCF sin estado que simplemente llame a MyProcess.Current.Start, MyProcess.Current.Stop, etc.

    La clase MyProcess controla todo el "follón", los hilos, los procesos, el estado del proceso para poderlo reanudar desde que se pausó, etc, etc.

    Cuando creas un objeto de la clase MyProcess se crea en el estado Stoped, cuando se le llama al metodo Start arranca un nuevo hilo o Threading.Task que empiez a hacer el trabajo, y después de cada bocado, si es que el bocado no se puede cancelar, mira si se ha pedido pausarlo, pararlo o lo que sea. Si se ha pedido pausarlo guarda el  punto en el que se ha quedado para poderlo reanudar en el punto en el que se quedo.

    El método Pause tiene que esperar a que el hilo se pause, probablemente usando un AutoResetEvent o un ManualResetEvent.

    En fin te queda un trabajo difícil, siempre que trabajas con hilos y procesos en segundo plano controlables es un infierno.

    ¡Ánimo!



    Jesús López


    EntityLite a lightweight, database first, micro orm

    domingo, 4 de enero de 2015 20:07
  • <<

    Ahora bien, ese modelo tiene algunos problemas que habría que solucionar, como por ejemplo la actualización de objetos desde un hilo distinto a donde se crearon. Por eso os planteé la pregunta con la esperanza de que alguien hubiera enfrentado un escenario similiar y no tener que reinventar la rueda.

    <<

    Pues mira, yo me he enfrentado a este tipo de problemas, multithreading y shared state. 

    La clave está en la sincronización, hay muchos objetos en .NET Fx para la sincronización, Monitor con la palabra clave lock. ManualResetEvent, AutoResetEvent, SpinLock, etc. Y algunas estructuras de datos thread safe que puedes encontrar en el espacio de nombres System.Collections.Concurrent. 

    El problema está que la programación multihilo es TREMENDAMENTE DIFÍCIL hacerla correctamente, hay libros enteros sobre el tema, se pueden escribir miles de páginas sobre el tema y por supuesto no es algo que se pueda plasmar en una respuesta de un grupo de noticias.



    Jesús López


    EntityLite a lightweight, database first, micro orm

    domingo, 4 de enero de 2015 20:24
  • >>tendré que usar alguna forma de hilos pero aún no he llegado hasta el punto de decidir cómo hacerlo

    pero para que quieres usar hilo si WCF ya lo implementa

    si analizaste el link que puse veras que al nostrar un servicio puedes indicar s el servicio atiende multiples instancias, con esto ya no necesitas que tu aplicacion implemente de forma explicita threads

    El modelo que intento implementar es instanciar el ServiceHost, hacer Open, entrar en un ciclo que, dependiendo de un estado, espera sin hacer nada (antes del Start o después de un Pause), procesa algunos datos (luego del Start o Restart) o sale del ciclo (luego del Terminate), cierra la comunicación y termina.

    no entendi

    como es eso de espera sin hacer nada antes del start ? sino iniciaste es logico que no haga nada

    no veo donde interviene WCF ? no veo que menciones en ningun lago la interaccion de un cliente que invoca la funcionalidad del servicio?

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    domingo, 4 de enero de 2015 20:42
  • Buenos días,

    según lo que planteas, creo que una posible solución sería crear un servicio auto hosteado con una máquina de estados (Workflow State Machine).

    http://channel9.msdn.com/Shows/Workflow-TV/endpointtv-Getting-Started-With-WF4-State-Machine

    http://workflowdemo.codeplex.com/

    Hay bastante documentación sobre cómo implementar máquinas de estados con Workflow Foundation. Yo no descartaría investigar lo que te propongo.



    Si se solucionó tu consulta no olvides marcar la respuesta. Si te ayudó vótala como útil. Saludos

    lunes, 5 de enero de 2015 9:36
    Moderador