Archive for the '7338' Category

Web Crawler usando el nuevo AjAgents

Tuesday, November 9th, 2010

Hace poco escribí una nueva implementación de mi proyecto AjAgents, descripta en:

AjAgents: a new implementation
AjAgents: una nueva implementación

Ahora, quiero describir una aplicación de prueba, de nuevo el Web Crawler: visitar y bajar la página desde una URL inicial, ir descubriendo enlaces al mismo sitio, y procesar el resto de las páginas. Anteriores versiones:

Web Crawler Using Agents and AjSharp
Web Crawler usando AjSharp
Distributed Web Crawler using AjMessages
Web Crawler distribuido usando AjMessages

La nueva versión del web crawler usa la nueva interface:

    public interface IAgent<T>
    {
        void Post(Action<T> action);
    }

En vez de escribir un tipo que hereda o implementa un agente, éste es un “wrapper” aplicado a un tipo normal cualquiera de .NET.

La nueva implementación de AjAgentes, y el ejemplo de web crawler (Proyecto AjAgents.WebCrawler), puede ser bajado desde:

http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjAgents

En una de mis anteriores implementaciones tenía:

Ahora, descarté el Dispatcher. Tengo tres clases:

El Resolver recibe un enlace a bajar y procesar. Decide si lo procesa o no (puede que el enlace ya haya sido procesado, o que el link no corresponda al sitio de partida, o se alcanzó un límite en la profundidad de enlaces desde la página inicial de proceso). Si el enlace es aceptado, la inforación es enviada al Downloader. Este objeto baja el contenido de la página, y lo envía al Harvester (podría enviarlo a otros agentes, también). El Harvester examina el contenido, extrae nuevos enlaces para procesar, y a cada uno lo envía al Resolver.

Estas clases son tipos .NET. Pero las referencias que mantienen entre sí son del tipo Agent<Downloader>, Agent<Harvester>, Agent<Resolver>. El código que arma un grupo inicial de objetos/agentes interconectados está en Program.cs:

       static Agent<Resolver> MakeAgent()
        {
            Resolver resolver = new Resolver();
            Harvester harvester = new Harvester();
            Downloader downloader = new Downloader();
            Agent<Resolver> aresolver = new Agent<Resolver>(resolver);
            Agent<Harvester> aharvester = new Agent<Harvester>(harvester);
            Agent<Downloader> adownloader = new Agent<Downloader>(downloader);
            resolver.Downloader = adownloader;
            harvester.Resolver = aresolver;
            downloader.Harvester = aharvester;
            return aresolver;
        }

 

La aplicación de consola AjAgents.WebCrawler aceptar un parámetro, con el enlace inicial:

AjAgents.WebCrawler http://ajlopez.zoomblog.com

Su salida:

Este ejemplo es un “proof of concept”. Me gustaría mejorarlo:

Agent<IDownloader>, que haga balanceo de carga con varias instancias de Agent<Downloader>.

Agent<T> instanciado en diferentes máquinas, implementando así un web crawler distribuido.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

AjAgents: una nueva implementación

Thursday, October 14th, 2010

Hace ya un tiempo, escribí sobre mi proyecto AjAgents, describiéndolo, implementando algoritmos genéticos y otras demos mínimas. El proyecto se basa en el envío de mensajes a “agentes” (podría llamarlos actores, o algo así). Los agentes reciben los mensajes y los procesan uno por uno. Cada instancia de un agente no necesita manejar la concurrencia: los mensajes son encolados en una cola interna por cada agente (podría más adelante, reimplementar esto usando una cola compartida entre varios agentes, o consumida por un pool de threads fijo que vaya consumiendo trabajo pendiente de esa cola, derivándo cada uno al agente destino).

Pero mis anteriores implementaciones se basaban en el uso de Reflection, o, alguna vez, en la librería CCR de concurrency de Microsoft Robotics (ver mis anteriores posts para más detalles). Ahora, tomo otro camino: un agente es un wrapper alrededor de un objeto normal. El código fuente puede ser bajado desde mi  proyecto AjCodeKatas Google Code, en el directorio trunk/AjAgents.

La interface principal, clave, es:

    public interface IAgent<T>
    {
        void Post(Action<T> action);
    }

La nueva idea: usar una clase genérica. T es el tipo de nuestro tipo “clásico” (que no es un agente), y Post es el método para invocar una acción sobre el objet interno: una acción que es una “rutina” que recibe un parámetro instancia de tipo T. De esta manera, le indicamos al objeto interno. Si éste tiene un método:

inner.DoSomething(parameter);

puede ser invocado como un mensaje desde el agente como:

agent.Post(x => x.DoSomething(1));

Esta es la implementación base de agente:

    public class Agent<T> : IAgent<T>
    {
        private T instance;
        private AgentQueue<T> queue;
        private bool running;
        public Agent(T instance)
        {
            this.instance = instance;
        }
        public void Post(Action<T> action)
        {
            if (!this.running)
                this.Start();
            this.queue.Enqueue(action);
        }
        private void Start()
        {
            lock (this)
            {
                if (this.running)
                    return;
                this.queue = new AgentQueue<T>();
                Thread thread = new Thread(new ThreadStart(this.Execute));
                thread.IsBackground = true;
                thread.Start();
                this.running = true;
            }
        }
//...
    }

Noten que el método Post encola la acción en la cola interna del agente. El proceso de esta cola está en el método .Execute (que no se muestra arriba). La cola es una cola bloqueante (sé que hay algo así en .NET 4.0, pero escribí mi implementación para poder ejecutar todo esto en 3.x, o aún 2.x):

    public class AgentQueue<T>
    {
        private Queue<Action<T>> queue = new Queue<Action<T>>();
        private int maxsize;
        public AgentQueue()
            : this(100)
        {
        }
        public AgentQueue(int maxsize)
        {
            if (maxsize <= 0)
                throw new InvalidOperationException("AgentQueue needs a positive maxsize");
            this.maxsize = maxsize;
        }
        public void Enqueue(Action<T> action)
        {
            lock (this)
            {
                while (this.queue.Count >= this.maxsize)
                    Monitor.Wait(this);
                this.queue.Enqueue(action);
                Monitor.PulseAll(this);
            }
        }
        public Action<T> Dequeue()
        {
            lock (this)
            {
                while (this.queue.Count == 0)
                    Monitor.Wait(this);
                Action<T> action = this.queue.Dequeue();
                Monitor.PulseAll(this);
                return action;
            }
        }
    }

Apliqué TDD para el desarrollo de todas estas clases, un ejemplo de test:

        [TestMethod]
        public void InvokeIncrement()
        {
            ManualResetEvent handle = new ManualResetEvent(false);
            Counter counter = new Counter();
            Agent<Counter> agent = new Agent<Counter>(counter);
            agent.Post(c => { c.Increment(); handle.Set(); });
            handle.WaitOne();
            Assert.AreEqual(1, counter.Count);
        }

Mucho del código fue derivado (bueno… casi copy and paste 😉 de mi trabajo previo con AjSharp (channels, agentes, queues, goroutines… ).

Tengo preparada una demo “prueba de concepto” usando esta nueva implementación de AjAgents. Es mi “clásico” ejemplo de Web Crawler. Pero eso es tema para otro post.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Recursos de Grid Computing

Saturday, May 10th, 2008

Desde el año pasado, y más en estas últimas semanas, he estado investigando sobre Grid Computing, buscando enlaces, recursos, “papers”, implementaciones. Este post es el resultado de esa investigación.

Como siempre, un artículo de la Wikipedia:

http://en.wikipedia.org/wiki/Grid_computing

Si Ud. se está iniciando en el mundo de Grid Computing, estas son buenas introducciones

New to Grid Computing

Grid Computing according IBM

The anatomy of the grid

The physiology of the grid  

Interesante lista de lecturas para desarrolladores en Grid

Recommended reading list for grid developers

Grid Café tiene varios artículos y recursos

Grid Cafe Grid Projects in the world  

Grid Cafe The place for everybody to learn about the Grid  

What is “the Grid”?  

Grid @ CERN 

Sobre el estado de la industria:

http://www.gridtoday.com/ (un poco demasiado abarcativo, no es sólo sobre grid computing)

http://www.gridblog.com/

La lista completa de enlaces que mantengo, en:

http://del.icio.us/ajlopez/gridcomputing

Algunos productos para examinar:

http://www.gridgain.com
http://www.digipede.net algo más sobre Digipede en http://dotnetjunkies.com/WebLog/stefandemetz/archive/2006/12/09/Free_Grid_Computing_software.aspx
http://www.gridgistics.net/

http://sourceforge.net/projects/ngrid/

Algo relacionado, que comienza a “estar de moda”:

http://en.wikipedia.org/wiki/Cloud_computing

He escrito artículos en este blog sobre Grid Computing:

http://msmvps.com/blogs/lopez/archive/tags/Grid+Computing/default.aspx

y en “Anglish”, Angel’s English:

http://ajlopez.wordpress.com/category/grid-computing/

donde hay algunas implementaciones sencillas de ideas a seguir explorando, como AjAgents, y AjMessages.

Esta lista de recursos, en Anglish, desde:

Grid Computing Resources

Angel “Java” Lopez
http://www.ajlopez.com

Agentes en Grid

Thursday, May 8th, 2008

El año pasado participé del desarrollo de una aplicación que se ejecuta en una grilla de máquinas sin disco. Este año, estoy volviendo a actualizar el proyecto, espero poder bloggear sobre el resultado dentro de un mes. Mientras, quisiera escribir sobre algunas ideas a explorar.

En este post, uso el término “agente”, de una forma algo libre. No definiré precisamente el concepto, quiero usarlo como término base a refinar en el futuro (llegando en algún momento a tratar el tema de agentes autónomos, que me parece más interesante). Por ahora, exploremos algunas ideas básicas (¿ingenuas?) para entender mejor los problemas relacionados con agentes y aplicaciones en grid. Algunas de las ideas acá presentadas pueden ser vistas como ingenuas, pero siento que es un ejercicio necesario, para aprehender los conceptos clave y los problemas a ser resueltos en este tipo de aplicaciones. Al final de este post, presentaré algunas sugerencias de implementación.

He descripto algunas aplicaciones para ejecutar en una grid en mis anteriores post:

Grid Computing Programming

Más programando para una grid

Programando para una Grid

Conceptos de Agentes

En este post, un agente es una pieza de software, con conducta y estado. Se ejecuta en un host de agentes, una aplicación que provee los servicios de base para que el agente pueda “vivir” y trabajar. Representaremos al agente con esta figura:

Patrones de comunicación de agentes

Hay mucha literatura sobre comunicación de agentes, desde simples técnicas hasta elaborados contratos, negociaciones, y más. Podemos tener agentes con creencias, deseos e intenciones. En este post, un agente es más simple: sólo tiene estado, puede enviar y recibir mensajes. Puede recibir estímulos de otros agentes y desde el ambiente de su host.

El más simple patrón de comunicación es un agente enviando un mensaje a otro agente:

Algunas notas:

– El agente enviador conoce al agente receptor. Quiero decir, alguna forma de identidada debe ser implementada. El mensaje no es enviado a cualquiera: el que envía intenta enviar el mensaje a un determinado agente.

– El mensaje transporta datos, y debe ser entendido por el receptor, posiblemente procesado por uno de sus métodos de implementación.

– En enviador no espera por una respuesta. No está interesado en un mensaje de respuesta inmediato.

– Los agentes pueden residir en diferentes máquinas en la grila, y la comunicación se produce tanto local como remota.

Durante su vida un agente puede enviar muchos mensajes a distintos agentes, que debe conocer de alguna manera:

Algunas veces, el agente enviador recibirá un mensaje desde el agente receptor, notificando algun trabajo hecho, o enviando algún dato procesado. Dependiendo de la aplicación, el mensaje de respuesta podría acarrear información para identificar al mensaje original:

En este caso, el enviador original debe estar preparado para recibir la respuesta de una manera asincrónica. Esto podría ser un interesante problema a resolver: un agente puede enviar varios mensajes, y sería mejora si puede seguir ejecutando sin recibir todas las respuestas a tiempo. Por ejemplo, en una aplicación de un juego de tablero, un agente puede delegar la exploración de un árbol de jugadas a otros agentes, y, luedo de un tiempo, sería posible tomar una decisión, con sólo algunas respuestas recibidas.

Nubes (“clouds”) en el cielo de la grilla

Otro caso: un agente puede estar interesado en enviar un mensaje, pero no a un receptor determinado. Al contrario, quiere enviarlo a una “nube” de agentes, así cualquiera interesado en el mensaje tendría la oportunidad de procesarlo.

Esta característica puede ser implementada usando estas estrategias:

– Un agente envía un mensaje a un sistema de pizarra (blackboard), que otros agentes están vigilando.

– Una agente enviaría una mensaje a la aplicación host, indicando un tópico (como en una cola de mensajes), así cualquier agente subscripto recibirá el mensaje. Una variante: sólo algunos subscriptores reciben el mensaje, dependiendo de parámetros de aplicación.

– Un agente podría enviar un mensaje dirigido a alguna definición de proveedor de servicio. Un proveedor de servicio es un agente, que declara al comienzo de su vida, sus capacidades y los servicios que puede proveer. 

Un ejemplo, tomemos una aplicación de un juego de tablero, ajedrez o go. Un agente en esa aplicación puede enviar un mensaje reclamando resolver cierta posición de ataque. En un sistema de pizarra, publicará el pedido. En un sistema de tópicos, lo enviaría al tópico “ataques”. En una estrategia de proveedor de servicio, envía el mensaje a uno o más de los proveedores del servicio AttackResolver.

Como en el patrón anterior, un agente puede recibir una respuesta asincrónica, ahora desde “la nube”:

 

Duplicación de agentes

Un agente tiene conducta y estado. Si el agente puede dividir su trabajo, podría tomar el camino de duplicarse a sí mismo:

Es algo extraño, pero podría ser útil, dependiendo de la aplicación a desarrollar.

Agentes y la Grilla

Cada agente puede ser albergado en un nodo de la grilla. El mecanismo de envío de mensajes debe ser capaz de enviar un mensaje a otro nodo. La aplicación host mantiene una lista de agentes por identidad, y conoce cúal  es local o remoto. Una prueba ácida: una aplicación de grilla con agentes debe ser capaz de correr en una sola máquina, o en una grilla, sin cambiar el código o el algoritmo.

 

Como en otras implementaciones de grilla (discutidos en los post que mencioné al principio), un servidor central está a cargo de la distribución de las tareas entre los nodos de la grilla. “Grid as a Service” es una nueva frase que podemos aplicar a esta situación.

Moviendo al Agente

Un agente puede iniciar sus actividades en un node. Pero en algún momento, puede decidir de continuar su trabajo en otro nodo (la aplicación host que lo alberga también puede tomar esa decisión, independientemente del agente). Entonces, su estado sería enviado de un nodo a otro (otro caso: podría querer duplicarse y que su clon siga el trabajo en otra máquina).

Noten que la conducta del agente (que puede estar compilada o puede estar escrita en un lenguaje dinámico o de agente), no viaja. Pero bien podría ser que viaje, incluso, que haya agentes que vayan adaptando su conducta en el tiempo.

Inyectando conducta

La conducta de cada agente podría ser expresada en código compilado (archivos .jar en Java, assemblies en .NET). Otras alternativas son posibles: la conducta podría ser especificada en un lenguaje de scripting dedicado a agentes (pienso en una adaptación del AjBasic, por ejemplo).

Si la conducta se expresa en forma compilada, uno o varios servers puede tomar la responsabilidad de almacenar y distribuir esos componentes:

Ideas de implementación

Muchas de estas ideas pueden ser implementadas en cualquier lenguaje/tecnología apropiada, como Java y .NET, que soporte múltiples threads, invocación remota, serialización de mensajes, etc…

En los últimos tiempos estuve trabajando en mis projectos AjMessages y AjAgents, más información enestos post:

AjMessages: a message processor

Agents using Concurrency and Coordination Runtime (CCR)

AjMessages- hacia un procesador de mensajes

Agentes usando Concurrency and Coordination Runtime (CCR)

Algoritmos Genéticos con AjAgents y Concurrency and Coordination Runtime (CCR)

Genetic Algorithms with AjAgents and Concurrency and Coordination Runtime (CCR)

(Tengo otro proyecto, AjGrid, no publicado aún). Para este post, creo que el AjAgents podría ser una implementación de esas ideas. AjMessages tiene ahora soporte de ejecución remota, pero está más orientado a un proceso tubería (“pipeline”): es más difícil de implementar en semejante sistemas las ideas de este post.

Estoy agregando algunas características a AjAgents (ahora, AjAgents trabaja sólo en local):

Configuración: Carga y creación de agentes en ejecución, según alguna información de configuración, ya sea al inicio o en el medio de la ejecución.

Assembly remoto: Así un nodo de grilla pueder ser inyectado con nuevos agentes.

Identificación de Agente: Para identificar al agente de manera única (un UID debería bastar).

Transporte de mensaje: Windows Communication Foundation es un candidato, otro podría ser DSSP.

Un posible camino es tomar Decentralized System Services (DSS) del Microsoft Robotics Developer Studio. Un agente podría ser implementado como un servicio de DSS, ejecutando en un host DSS. La comunicación entre máquinas puede ser implementada usando DSSP como protocolo.

“Stay tuned”, vendrá más código.

(Esta es una actualización y traducción de mi post en “Anglish”, Angel’s English:

Agents in a Grid

)

Angel “Java” Lopez
http://www.ajlopez.com/en

Algoritmos Genéticos con AjAgents y Concurrency and Coordination Runtime (CCR)

Sunday, April 13th, 2008

El año pasado había implementado un ejemplo con AjAgents usando CCR:

Agentes usando Concurrency and Coordination Runtime (CCR)
Agents using Concurrency and Coordination Runtime (CCR)

Escribí en mi blog en “anglish” (Angel’s English) algunas ideas para explorar:

Agents in a Grid

Recordemos que Concurrency and Coordination Runtime es parte de Microsoft Robotics Studio, puede leer más sobre esta librería en

Concurrent Affairs: Concurrency and Coordination Runtime

y consultar mis enlaces de Delicious

http://del.icio.us/ajlopez/ccr
http://del.icio.us/ajlopez/msrs

En estos días, extendí mi ejemplo AjAgentsCCR-0.1.zip con dos nuevos proyectos. Uno de consola AjAgents.Genetic01, y otro de ventanas AjAgents.WinGenetic01:

La idea básica es ejecutar un algoritmo genético usando agentes, los elementos de AjAgents, que se envían mensajes usando ports de CCR. Cada agente procesa sus mensajes entrantes, y envía mensajes salientes a otros agentes, en paralelo. El problema que encaro es el clásico Travelling Salesman Problem. Leo en la Wikipedia:

If a salesman starts at point A, and if the distances between every pair of points are known, what is the shortest route which visits all points and returns to point A?

La ventana de ejemplo es sencilla:

La clase Genoma representa el viaje (una lista de Points y un valor de distancia total):

class Genoma { public List<Point> travel = new List<Point>(); public int value; } class Point { public int x; public int y; }

Al comenzar la ejecución, los agentes son creados y conectados:

best = new BestSoFar(); evaluator = new Evaluator(); mutator = new Mutator(); collector = new Collector(); evaluator.Collector = collector; collector.Mutator = mutator; collector.Evaluator = evaluator; collector.BestSoFar = best; mutator.Evaluator = evaluator;

Un genoma inicial es creado:

Genoma genoma = new Genoma(); Random rnd = new Random(); for (int k = 0; k<40; k++) { Point pt = new Point(); pt.x = rnd.Next(12); pt.y = rnd.Next(12); genoma.travel.Add(pt); } genoma.value = evaluator.CalculateValue(genoma);

El agente BestSoFar dispara un evento. El formulario se registra como observador de ese evento, para refrescar el mejor viaje calculado y dibujarlo. El programa envía el genoma inicial generado al agente Mutator, varias veces, para generar la primeras poblaciones:

best.NewBest += BestGenoma; for (int k = 0; k < 60; k++) { mutator.Post("Mutate", genoma); }

Noten el uso del método Post, básico en AjAgents. Cada agente implementa ese método, que invoca un método en el agente, usando una puerta de CCR para invocar el método final. El método es ejecutado entonces no en el momento, sino en forma “paralela”, en un thread que maneja el pool de threads de CCR.

Un diagrama simple para explicar la interacción entre agentes:

El Mutator envía cada genoma al Evaluator. Este agente calcula el valor asignado a ese genoma. Lo envía a el Collector. Este implementa una nueva idea para este ejemplo: en vez de tener una clase Population o similar, el Collector recive genomas, y cuando tiene n o más genomas, determina los mejores del conjunto, envía el mejor a BestSoFar, y reenvía los mejores a Mutator, para generar una nueva lista de genomas a evaluar. Como método típico de ejemplo, tomemos uno de la clase Collector:

void ProcessList(List<Genoma> list) { list.Sort(comparer); bestsofar.Post("Process",list[0]); evaluator.Post("Evaluate",list[0]); evaluator.Post("Evaluate",list[1]); for (int k = 0; k < 4; k++) { mutator.Post("Mutate",list[k]); mutator.Post("Mutate",list[k]); } }

Tengo que mejorar el algoritmo genético. Ahora, sólo usa mutación, sin hacer “crossover” (cruzamiento) entre los mejores genomas. El resultado que consigue ahora, no es óptimo: puede dar como resultado un máximo local, pero no la mejor solución. La idea del ejemplo es ver ejecutando AjAgents con CCR, en una prueba de concepto.

Conclusiones

Uno puede ver cada agente como una pequeña célula u organismo, que reacciona a mensajes externos y envía mensajes a otros agentes. Cada agente está levemente acoplado a los demás. En estos ejemplos, los agentes son creados y relaciones por código, pero bien podrían ser creados y relacionados usando algún framework de inyección de dependencias, como Spring.NET o el nuevo Unity Application Block de Microsoft.

En lugar de usar solamente ports de CCR, podría escribir los agentes como  DSS Service components (ver Microsoft Robotics Studio para más información sobre DSS); el algoritmo podría ser distribuido en una grilla (“gridified”) y cada agente interactuar con otros, independientemente de su ubicación..

Pero eso es otra historia….;-)

En esta semana, ha sido anunciada la nueva versión de Microsoft Robotics Developer Studio 2008:

http://blogs.msdn.com/msroboticsstudio/archive/2008/04/09/microsoft-robotics-developer-studio-2008-ctp-april-available.aspx

La tercera nueva característica ahí anunciada, sería muy interesante para implementar “miniagentes” distribuidos:

Support for creating applications that run on multiple DSS nodes using Visual Programming Language and DSS Manifest Editor. This makes it much simpler to create applications that run across nodes, either on the same machine or across the network. When an application containing multiple nodes is to be started, VPL creates individual deploy packages for each node and fires them up across the network.

(este artículo es la versión en español de mi versión “anglish”:
Genetic Algorithms with AjAgents and Concurrency and Coordination Runtime (CCR)

)

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com/

Agentes usando Concurrency and Coordination Runtime (CCR)

Friday, October 19th, 2007

En estos días, escribí un ejemplo mínimo, para explorar algunas ideas, usando el Concurrency and Coordination Runtime, la librería de Microsoft library que viene en su Robotics Studio. Más sobre CCR en el artículo:

Concurrent Affairs: Concurrency and Coordination Runtime

Mi idea es implementar el pase de mensajes entre componentes, de manera asincrónica. El código de ejemplo puede bajarse de AjAgentsCCR-0.1.zip

El agente

En el ejemplo, la interfaz a implementar por cada componente, que llamamos agente, es:

public delegate void ReturnHandler<T>(T t); public interface IAgent { void Post(Message msg); void Post(string action, params object[] parameters); void Post<T>(string action, ReturnHandler<T> handler, params object[] parameters); }

El agente puede recibir mensajes (que son objetos de una clase Message, ver más abajo). En el mensaje viene un texto con la Action a ejecutar (como si fuera un nombre de método) y una lista variable de parámetros.

El tercer método Post implementa un delegado, un método a ejecutar cuando el envío del mensaje produce una respuesta, un valor de retorno. Esa vuelta se produce en el futuro, no se queda esperando la respuesta. Podemos enviar un mensaje a otro agente, sin esperar la respuesta, o podemos escribir el delegado para procesar esa respuesta cuando llegue.

El mensaje

Es una clase con

action: un nombre que identifica la acción asociada al mensaje

body: el contenido del mensaje

response port: la puerta CCR donde enviar el valor de retorno, si es necesario

He aquí el código de esta clase:

 

public class Message { private string action; public string Action { get { return action; } set { action = value; } } private object body; public object Body { get { return body; } set { body = value; } } private Port<object> returnport; internal Port<object> ReturnPort { get { return returnport; } set { returnport = value; } } public Message() { } public Message(string action) { this.action = action; } public Message(string action, object body) { this.action = action; this.body = body; } public Message(string action, object[] bodyvalues) { this.action = action; if (bodyvalues != null && bodyvalues.Length == 1) this.body = bodyvalues[0]; else this.body = bodyvalues; } }

Notemos que el mensaje tiene puede manejar un arreglo de valores en el body, y que puede enviar un objeto dentro de su contenido.

La implementación

En este ejemplo inicial, la interfaz IAgent tiene una implementación basado en Invoke, via Reflection, que termina invocando un método en el agente, que tiene el mismo nombre que la acción recibida. Se pueden pensar otras formas de manejar el mensaje, pero esta manera permite codificar el agente como una clase más, con métodos normales.

El mensaje, una vez recibido, se coloca en una puerta CCR, para que se pueda manejar de manera asincrónica. Hay un Receive arbiter de CCR para atender los mensajes entrantes, que son ruteados al correspondiente método en el objeto.

Creo que es una solución interesante: se puede escribir una clase con métodos normales, y convertirla en agente, heredando de la clase Agent.

Podemos enviar un mensaje con

agent1.Post("Decrement",20);

Puede “consultar” algún valor con:

agent1.Post<int>("GetCounter", PrintCounter);

Próximos pasos

Estoy trabajando en:

  • Escribir un Enviroment, o clase Host, donde los agentes “vivan”. El Host sería responsable de mantener una lista de agentes por nombre.
  • Escribir un archivo de configuración donde pueda especificar qué agentes crear y cargar a comenzar a funcionar el Environment o Host, y que los relacione usando alguna implementación de inyección de dependencias. Me imagino servidores de agentes que pueda cargar un agente remoto de assemblies que se encuentren en otros servidores de la red.
  • Usar estos agentes en forma distribuida, con Windows Communication Foundation (WCF), o con Decentralized System Services (DSS). En este último caso, un servicio DSS podría ser un agente o podría representar la entrada a un Environment
  • Implementar algún patrón Subscribe/Notify entre agentes.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com/