obra con: Microsoft Orleans Framework”. Espero poder aportarte los conocimientos mínimos y necesarios para que puedas iniciarte en este apasionante mundo. Jose María Flores Zazo, autor
Sección 5 Puntos importantes Detalles a tener en cuenta y conclusión Sección 3 Ciclos de Vida Teoría que dominar y como mejora tus actuales arquitecturas Índice Resumen
un proyecto Open Source que proporciona un modelo simple de programación que permite construir software que puede escalar desde una sola máquina a cientos de servidores. Podríamos asociar a Orleans como un runtime distribuido que permite desarrollar en .NET software capaz de procesar grandes volúmenes de datos y desplegar en la nube u on-premises. Orleans es un marco de trabajo que provee de muchas características de sistemas distribuidos. Pero antes debemos saber ¿qué es un Modelo de Actor?. Un Modelo de Actor se define como: un modelo matemático de computación concurrente que trata al “actor” como primitiva universal de las computación concurrente. Los frameworks de Modelo de Actor se basan en pasar mensajes entre actores, y los actores se crean según sean necesario. Estos modelos pueden aprovechar la concurrencia de procesadores con múltiples núcleos y las abstracciones del software. Esto quiere decir que son capaces de procesar millones y millones de mensajes en tiempo real o casi. ¿Qué es? – Un proyecto Open Source
crea Orleans, y estamos en 2022 cuando publico este libro. Este proyecto llama mi atención cuando comienzo a investigar y poner en práctica el Modelo de Actor con Dapr. Gracias a soluciones precocinadas como Dapr, veo que el uso de estas librerías para personas con poca experiencia logra crear soluciones distribuidas globalmente de forma robusta así como de alta disponibilidad y muy elásticas. Esta biblioteca logra una abstracción tal que nos evita mucha complejidad. Por último Orleans nos permite que un solo desarrollador pueda crear un entorno escalable sin tener a una persona experta en desarrollo de sistemas distribuidos. Otros marcos de Modelo de Actor requieren mucho más trabajo tales como la monitorización y el ciclo de vida. Con Orleans nos evitamos todo esto; Orleans es una solución muy probada, tanto que se usa en muchas aplicaciones IoT. Inicialmente, Orleans se construyó para poder ayudar a expandir la computación en la nube a los desarrolladores cuando la nube aun era algo muy nuevo. Orleans tiene la capacidad de proporcionar servicios interactivos hiper-escalables que admiten un alto rendimiento y disponibilidad con la baja latencia de la nube y los sistemas locales.
Actores existe para satisfacer unas necesidades técnicas; sin embargo, la sobrecarga adicional de conocimiento, experiencia, y la alta complejidad algorítmica hace que sea difícil lograr el objetivo del problema. Intentar satisfacer estos requisitos es un gran desafío y gracias a herramientas como Orleans, esto se hace más sencillo. Si nos vamos a las aplicaciones monolíticas que se usan y usaban para realizar múltiples proyectos, por ejemplo suelen usar un servidor donde tenemos la lógica de negocio de pedidos y envíos (estamos en una tienda on-line), esto es una mala opción, ya que cada elemento suele depender de un equipo de desarrollo, donde la comunicación no es todo lo fluida que debe con mucho acoplamiento y debería escalar de forma independiente, entre otras cosa casi siempre se vuelve compleja de mantener… pero no todo es malo en los monolitos. Los microservicios son aplicaciones de alcance granular que interactúan enter sí para completar un holístico comportamiento. Este termino se refiere al ámbito de trabajo que acopla débilmente la aplicación. Por ejemplo envíos y pedidos estará separados por su propio servicio. Desacoplar elimina la necesidad de escalar la aplicación como un todo, si no, con las piezas que nos interesen a demanda. Tambien nos permite diseñar sistemas complejos y desplegarlos de forma individual.
diversos microservicios y en todos ellos al final se usan arquitecturas consolidadas, en este caso Orleans viene a aprovechar al máximo la computación en la nube con una solución casi lista para montar, lo que nos permite centrarnos en el negocio y no tanto en la arquitectura. Los modelos de actores de Bernstein y Bykov (2016) nos permiten mantener los ciclos de vida, la elasticidad y concurrencia. Los acores, objetos aislados de grano fino que permiten enviar y recibir mensajes asíncronos. Esta fase poco te podrá decir, ya que suelen ser complicados de entender, complejos y no se suelen ver hasta que no tienes un problema e empiezas a indagar. Por regla general un sistema de actores necesita de un equipo con mucha experiencia y conocimientos… Aquí viene Orleans que nos ayuda a reducir esa barreras. Orleans nacen en 2015 por parte de Microsoft y con una gran comunidad apoyándolo. Orleans es una solución viable para crear servicios distribuidos, elásticos, mantenibles e independientes de la nube (Azure, AWS, …). Este proyecto es capad de permitir a un solo desarrollador, sin un conocimiento profundo de actores, crear proyectos descentralizados a nivel mundial sin requerir desarrolladores especializados. Además, la curva de aprendizaje es baja, permite que cualquier desarrollador comience a construir modelos desde ya.
actor(1/3) El término actor se puede definir para nuestro propósito: utilizar una aplicación como abstracción del procesador con actores que pasan mensajes asíncronos por concurrencia. Los actores realizan el trabajo dentro de estas tres funciones: 1. Comunicaciones definidas por otros actores. 2. Lógica realizada en el mensaje antes de pasarlo al siguiente actor. 3. Crear nuevos actores. En resumen: cada actor sabe como manejar el trabajo que se le envía y dónde se pasará a continuación el mensajes, además un actor puede crear otros actores. Los actores se comunican mediante colas y mensajes. Cada actor tendrá su cola, procesará sus mensajes y los pasará al siguiente actor. Práctica común de procesar un solo mensaje a la vez. Los actores estan en un lugar aislado, es decir, que no necesitan compartir el acceso, de modo que si una instancia falla, no afectará a las demás instancias. Una vez completada la lógica, el actor para el mensaje de forma asíncrona a la cola del actor existente/creado. Cada actor se define de una forma única mediante un GUID o una URI. La garantía de entrega de mensaje determinará la confiabilidad de la entrega de mensajes. Existen tres tipos de garantías que varían en torno al uso del recursos. Actor – Partícipe de una acción o suceso
actor(2/3) 1. At-Most-Once, como máximo una vez. El mensaje se entregará cero o una vez. Esto es lo que implementa Orleans. Un mensaje nunca se entregará dos veces, pero se entregará una o ninguna vez. 2. At-Least-Once, al menos una vez. Se realizan varios intentos de entrega. 3. Exactly-One, exactamente una vez. Se entregará un solo mensaje cada vez, lo que incide en los gastos de un recurso. Actor Mensaje Mensaje Actor Mensaje Actor Estado aislado Mensaje Buzón de correo electrónico
actor(3/3) Un actor puede hacer todo el trabajo o enviarlo a otro para que complete el trabajo. Dependerá del diseño arquitectónico y la implementación. Se puede llamar al actor adicional para que procese un trabajo específico, como un microservicio. Los actores pueden tener fácilmente miles e incluso millones de transacciones concurrentes en una red global. Los framework de actores son comúnmente usados en IoT, IA y juegos. Pueden considerarse como una clase o varias clases que contienen lógica de negocio para crear una red que permiten computar para un mismo propósito.
con Orleans(1/4) El modelo esta basado en cualidades de granularidad para ahorrar en gastos, colas, mantener asincronía y concurrencia. La reducción del gasto se debe a la gestión del ciclo de vida de los actores, denominados actores virtuales o granos dentro del framework de Orleans. Los ciclos de vida son un trabajo tedioso para cualquier desarrollador ya que la configuración de la activación, espera entre trabajos y saber cuando desactivar una tarea, no esta al alcance de cualquier desarrollador. Al mismo tiempo los desarrolladores deben, saber inicia una nuevo actor o se extralimita en su tiempo, o gestionar uno que acapara recursos. Estos son los retos a los que se enfrenta cualquier persona que construye sistemas distribuidos. Orleans reduce estos requisitos de bajo nivel del ciclo de vida del grano. Es decir, que no necesitas personas altamente cualificadas, ya que lo que nos permite Orleans es centrarnos en la lógica de negocio. Como decía antes, incluso una sola persona será capad de dejar un proyecto bien armado, con un riesgo mínimo gracias a este framework. Ya he comentado que la garantía de entrega del mensaje es de 0 o 1 vez. Esto significa que se pueden perder, aunque se devuelva un mensaje de reconocimiento cuando el mensaje se entrega. Basado en granularidad – Reducción de gastos
con Orleans(2/4) Orleans mantiene actores virtuales (en memoria). Para poder diferenciarlos de los actores tradicionales, se usa ese término o bien granos. Los silos manejan los granos. Los silos son las aplicaciones de administración de recurso distribuidos que se ubican entre los clientes y los granos , realizan la verificación de los granos para garantizar la confiabilidad. Administración de Recursos Ciclo de vida Gestión de Recuperación Gestión de Fallos Guardado del Estado Creación del Actor Silo Creación de un Actor Virtual Interface Actores Necesidad de un sistema experto y recursos de bajo nivel. Actores Virtuales Elimina cualquier complejidad de bajo nivel.
con Orleans(3/4) Podemos usar grupos que son múltiples silos que pueden comunicarse y funciona entre ellos. Los silos pueden crear granos o enviar mensajes a los granos ya activos en nombre de la solicitud del cliente. Además, monitorizan la salud de los granos, y en caso de fallo, reactivación o recolección de basura, un silo puede necesitar un reinicio, o inicializar nuevos granos. Los silos tambien pueden solicitar granos a través de otros silos y por tanto pueden compartir comunicaciones. Los granos tambien pueden compartir átomos, consistentes, aislados y duraderos (ACID). Las transacciones tienen aislamiento serializable, es decir, que esta más aislado. Adémás, los controles de concurrencia basados en bloqueos eliminan las colisiones de escritura. Las transacciones en Orleans no utilizan un administración de transacciones centralizado (un orquestador), que limitaría el rendimiento y la escalabilidad. Los granos admiten secuencias administradas (Managed Streams)y disparadores por tiempo (Time Triggers). Los streams son ampliamente usados para sistemas de colas. Tambien admienten procesamiento de lotes con colas y puntos de control. Los puntos de control permiten que la transmisión está al tanto de los últimos elementos eliminados para serguir enviando el elemento siguiente de la cola.
con Orleans(4/4) Tanto Azure, como AWS u otras nubes pueden usar Orleans a través de colas y proveedores de streams. Tambien existen otro mecanismo llamado recordatorios (Reminders) que junto a los temporizadores pueden programarse (schedule) incluso cuando el grano no está activo. Es decir, que podemos programar trabajo para lotes o informes diarios. En resumen, los podemos programar para realizar tareas periódicas. Es agnóstico de la nube y debido a que Orleans usa .Net Core y .Net 6, nos permiten usarlo tanto en Windows como Linux o Mac. Eliminando la barrear y dependencia de un host o nube. Las interfaces, es importante comentarlo, hacen referencia a todos los granos para mantener el desacoplamiento basado en principios SOLID. Los granos y silos se llaman unos a otros basándose en interfaces que heredan el desacoplamiento y preparan el escenario para las pruebas unitarias. Nos voy a entrar a discutir los principios SOLID, supongo que ya los conoce, en caso contrario puedes visitar cualquier artículo al respecto y ponerte al dia. A nivel de arquitectura un solo apunte sobre SOLID, podemos hacer que las interfaces se incrusten dentro de la aplicación cliente o bien en un cliente proxy, permitiendo que otros clientes llamen al los endpoints del proxy sin estar acopladas las interfaces a los granos.
los Granos Orleans implementa ciclos de vida automatizados para los granos. Esto reduce la sobrecargar en el equipo de desarrollo y hace que la implementación de actores sea mucho más sencilla. Este aspecto da mucha potencia a Orleans. Nos simplifica la forma de trabajar con frameworks distribuidos. Otros framework como Akka, depende de que los desarrolladores dediquen recursos a como activar y disponer de instancias, tambien no exige que exista un mecanismo para determinar si la instancia esta activa o no, alargar la vida para que se complete el trabajo, etc. Implementación automatizada – Orleans nos resuelve el problema
lo he dicho antes, los gastos decrecen exponencialmente, hasta el punto que una sola persona puede crear una solución distribuida. Esto nos permite centrarnos en la lógica de negocio y no en las configuraciones y codigo complejo. Por ejemplo si quieres usar Erlang en vez de Orleans, tendrás que crear una arquitectura muy minuciosa. La aplicación Erlang necesita ser preparada cuidadosamente antes de construir los elementos estructurales, como los tiempos de los actores y aplicar el código de nivel básico antes de implementar la lógica de negocio. En cambio, Orleans tiene estos elementos incorporados en el marco y permite a los desarrolladores configurarlos según sea necesario. En resumen Orleans nos permite ponernos manos a la obra con los que realmente importa desde casi el minuto 0. Tu podrás con todo – No necesitas personas altamente cualificadas
desde su creación en 2010, ha demostrado fiabilidad en entornos de producción. Parece que os estoy dando algo obsoleto, pero esto casi seguro que poca gente conoce esto y si estas con Dapr como yo te habrá picado la curiosidad. Estoy seguro que alguna de estas aplicaciones has usado y no sabías que se basaban en este framework: • Microsoft Skype. • Microsoft Hallo 4 y 5. • Gears of War 4. • Honeywell para su plataforma de IoT. Y en la actualidad se está usando en: • Microsoft Studios. • Visa. • YouScan. • DigitalOceans • Atlas Reality. Inc. Como veis no esta nada muerto, puedes ir a GitHub y luego meterte en el Discord de Orleans. Un poco de historia – Nos ayudará a ver que cosas hemos usado o usan Actores
crear aplicaciones nativas en la nube, elásticas y de alta disponibilidad. Nos permite aprovechar un framework de actor con concurrencia, elasticidad y alta disponibilidad, sin ser expertos en sistemas distribuidos. Aquellas aplicaciones de IoT, bolsas de valores, correo electrónico son casos de uso de manual de los framework de actor. Lo que realmente debes tener en cuenta es que un solo desarrollador con conocimientos de codificación, algunos paquetes NuGet y el framework de Orleans, te permitirán implementar soluciones de alta complejidad, como si fuera un juego de niños. Sencillez – Facilitarnos el trabajo
palabras mágicas Los framework de actores se pueden aprovechar de las características innatas de las nubes: la distribución y la concurrencia. Orleans, nos permite usar distintas nubes, es multi-cloud. La elasticidad y disponibilidad son el resultado de varios factores en los objetos granulares que colaboran en un marco que supervisa y mantiene los cluster. La elasticidad se da principalmente por: • Tamaño del grano. Los granos no estan restringido a tamaño, pero si se construyen a tamaño monolítico y no implementan no podrá ser todo lo elástico que quisiéramos. Es mejor ir a los microservicios que nos permite una activación más rápida y por tanto ejecución. • Monitorización de la salud. Los silos monitorizan la salud en cada cluster. • Ciclos de vida. La orquestación inteligente de los ciclos de vida permiten la posibilidad de que los recursos estén disponibles cuando los granos necesitan ser creados. Orleans puede eliminar el usos de herramientas adicionales en la plataforma, como la gestión de la cache o un API. Los cluster y silos los podemos desplegar en Kubernetes. Los silos estan disponibles sin gestiones de red, cortafuegos o herramientas de terceros. Pero claro, algo tenía que tener: Orleans nos hace un vendor lock-in que es .NET. Mientras que la filosofía de los microservicios es poder hacerlos en cualquier lenguaje… Tenga en cuenta esto. Problemas de los sistemas distribuidos – Los que debemos resolver
los proyectos deben ser tan grandes como los que he mencionado antes. Para ver si Orleans se alinea con tu proyecto puedes considerar las siguientes restricciones: • Cientos o incluso billones de objetos que estan débilmente acoplados. • Entidades atómicas que pueden ejecutarse en un solo subproceso. • Actividades interactivas, como son resquest & responses. • El requerimiento de crecimiento en más de un servidor. • No se requiere un manejo holístico ya que los granos mantienen su funciones. Estos casos de usos cumplen al 100% las anteriores restricciones: • IoT. Miles o millones o … de señales de dispositivos que se comunican con el cluster, que desencadena eventos, que mantienen transacciones individuales a escala global, alta concurrencia, elasticidad, … IoT es el más representativo, cada grano representa un dispositivo IoT, IoT esta descentralizado y requiere concurrencia de transacciones consistentes des de cada dispositivo. • Mensajería Instantánea. Se necesita organizar gran cantidad de transacciones para los clientes que pueden estar en cualquier parte del mundo. Los granos representan a cada grupo. Algo ya hemos visto – Repasamos los principales casos de uso
Los jugadores viven en todo el mundo, las estadísticas de juego deben ser muy elásticas, ya que deben mantener salas de jugadores, personajes, etc. Etc. Solo tienes que pasarte por el grupo de Discord (job) y ver la cantidad de trabajos ofertados al respecto. • Reservas. Se requiere elasticidad, descentralización, concurrencia, alta demanda, … Las aerolíneas y los hoteles tienen alta demanda durante las vacaciones, y la fluctuación suele ser repentina. Los clientas además, pueden realizar las transacciones desde cualquier parte del mundo, y evitar una doble reserva, para ello los actores virtuales se comunican entre sí y con la base de datos para resolver el problema. • Bolsa de valores. Este software requiere alta concurrencia, elasticidad y velocidad para calcular precios o estadísticas. • Juegos de azar. Es como la bolsa de valores. • Telecomunicaciones. Para conmutación de nodos, servicios, torres… deben crear sistemas simultáneos que permitan flexibilidad. • Correo electrónico. Solución escalable y robusta que pueda pasar por diferentes protocolos.
anterior dirección podrás encontrar un tutorial que podrás seguir y ver como un solo desarrollador es capad de poner en práctica Orleans. Las paquetes de NuGet son: Donde Microsoft.Orleans.Server y Microsoft.Orleans.Client son los meta-Packages que nos dan las dependencias que probablemente usaras en los Silos y Clientes. Ejemplo – https://dotnet.github.io/orleans/docs/tutorials_and_samples/tutorial_1.html Project Nuget Package Silo Microsoft.Orleans.Server Silo Microsoft.Extensions.Logging.Console Client Microsoft.Extensions.Logging.Console Client Microsoft.Orleans.Client Grain Interfaces Microsoft.Orleans.Core.Abstractions Grain Interfaces Microsoft.Orleans.CodeGenerator.MSBuild Grains Microsoft.Orleans.CodeGenerator.MSBuild Grains Microsoft.Orleans.Core.Abstractions Grains Microsoft.Extensions.Logging.Abstractions
casi seguro que lo usaras en todas partes. Se incluye en Microsoft.Orleans.Server y Microsoft.Orleans.Client. Microsoft.Orleans.CodeGenerator.MSBuild genera automáticamente el código que se necesita para realizar llamadas a granos a través de los límites de la máquina. Por lo tanto, es necesario tanto en proyectos Grain Interfaces como Grains. Sitios donde encontrar más información: • https://docs.microsoft.com/es-es/dotnet/orleans/ • https://gitter.im/dotnet/orleans • https://aka.ms/orleans-discord • https://dotnet.github.io/orleans/ • https://twitter.com/msftorleans?lang=es
Este framework puede ejecutarte en local, en cualquier nube que admita .NET o se puede desplegar en Kubernetes. Podemos poner en práctica DevOps, lo que permite CI/CD. Se actualiza constantemente, a la hora de escribir este monográfico esta en beta la versión 4.0 de Orleans que es capada de aprovechar las nuevas características de .NET 7. Gestión de recursos y ampliaciones Ya he comentado que el ciclo de vida de Orleans gestiona los recursos mediante activación y desactivación de granos. Por tanto, estamos hablando de la capacidad de escalar en función de diversas medidas de los recuros: CPU, transacciones, etc. Es capad de funcionar en aplicaciones monolíticas y de microservicios, logicamente el tiempo de activación/desactivación es menor en un microservicio. Nos evita el escalado horizontal ya que las instancias de granos son creadas en tiempo de ejecución. Características – Resumen
debemos ser capaces de gestionar los fallos, algo nos ayudará la nube pero esta en nuestras manos usar técnicas más allá de la salud y recuperación de la nube. Lo habitual es usar: reintentos, recuperaciones de tipo roll-back, restablecer transacciones y volver al principio de la misma, control en los reintentos para evitar duplicidades, etc. Una lista recogida en Orleans: https://dotnet.github.io/orleans/docs/deployment/handling_failures.html Streaming Nos permite un calculo independen diente en cada grano del flujo, lo que otorga una potencia que nos permite tratar en cada parte del flujo los datos como queramos. Se puede usar Kafka, EventHub, Apache Spark, … Los sistemas actuales usan un modo unificado pero Orleans nos permite tratarlo de forma individual otorgando le poder de hacer lo que deseemos en cada punto del programa.
Orleans sigue los siguientes requisitos: • Flujos flexibles. Nos da le poder de elegir como configurar un flujo. • Topologías dinámicas. Podemos cambiarlas en tiempo de ejecución. • Granularidad fina en los flujos. Nos permite una gestión final de flujo y no una unificada. • Distribución. Soporta una gran cantidad de flujos, elasticidad, recuperación rápida ante falos, eficiente con los recursos y respuesta virtual en tiempo real. Mas información: https://dotnet.github.io/orleans/docs/streaming/streams_why.html Persistencia Bases de datos. Orleans permite almacenar el estado del Grano en Azure Storages, SQL, … pero esto no es para todo aunque permita almacenar l estado del grano y al activase recoja los datos, tienes que tener en cuenta que todo no se puede hacer y posiblemente tendrás que trabajar con acceso nativo en los granos. Aquí Dapr y su implementación de actores no da una flexibilidad enorme. Por ejemplo, patrón para cache: https://github.com/OrleansContrib/DesignPatterns/blob/master/Smart%20Cache.md
nos simplifica el desarrollo de sistemas distribuidos. Los ciclos de vida se crearon para eliminar abstracciones de bajo nivel, por tanto, disminuye la barrera de aprendizaje y el riesgo para los desarrollos. Es decir, que estos ciclos de vida, nos permite, como ya he dicho, centrarnos en la lógica de negocio. Simplicidad – Gracias a Orleans Activado Desactivar Persistir Activando Grano
estar en uno de los cuatro estados: activado, activando, desactivado o persistente. En detalle: Activando. Ocurre cuando llega una solicitud para el grano que no se encuentra activo. Se instancia al grano en memoria. Por ejemplo, el se solicita un instancia del grano y por tanto el silo activara de forma lazy el grano. El grano se inicia en memoria. El grano se activa y por tanto ya puede servir a la solicitudes. El grano se mantendrá activo en función de las peticiones. Esto permite a los desarrolladores programar como si el grano siempre estuviera activo. Activo. Todos los granos estan activo en memoria. Mientras cumple sus trabajo, los registros se mantienen en la base de datos que elijamos. El grano esta activo en memoria y estará listo para aceptar trabajo, incluso si no hay nuevas solicitudes. Esto se creo para poder atender a solicitudes adicionales: • Si el grano esta ocupado, el mensaje se almacena en una cola hasta que el grano este listo. • La concurrencia se basa en turnos que mantienen las tareas en el mismo hilo del grano que la creó. Las nuevas tareas e ejecutan en el mismo hilo que el padre según el programador de tareas TPL. Eso significa que solo se permite una ejecución y además e quedan a la espera para eliminar los hilos paralelos. Desactivación. Cuando el grano dejar de recibir solicitudes por un periodo de tiempo, entonces se envía un estado de desactivación. Este estado esta en preparación para ser persistido en la base de datos y eliminado de memoria.
un estado persistente donde us estado final es almacenado en una base de datos. Esto libera recursos para otros granos y reducirá la necesidad de más recursos, por tanto baja el coste en la nube. Este ciclo de vida nos da el poder del escalado en base a las necesidad en tiempo real. He hablado de silo, pero no de su ciclo de vida. Es similar (más adelante lo vemos con más detalle): 1. Primera etapa del ciclo de vida de un servicio. 2. Inicializa el entorno del runtime y el silo inicializa los hilos. 3. Se inicia el runtime, el silo inicializa agentes en nuestra red. 4. Inicializa el runtime del almacenamiento. 5. Inicia los runtimes de los granos, que incluye la gestión del grano, el servicio de pertenencia y el directorio de granos. 6. Inicia la capa de aplicación. 7. El silo entra en un cluster. 8. El silo se activa en el cluster y acepta carga de trabajo. 9. Ultima etapa del ciclo de vida.
mientras dura una llamada, incluso mientras esperan a tareas muy largas. Un grano vuelve a entrar en juego gracias a que nos permite anular la concurrencia basada en turnos y puede procesar múltiples mensajes un solo hilo. Esto nos permite que un grano acepte nuevo trabajo mientras el proceso actual esta esperando a que se complete el proceso asíncrono. Es decir, si estamos llamando a una base de datos de forma asíncrona otra petición puede empezar a procesar mientras espera respuesta. Esto tiene un riesgo: compartimos hilos y puede ocurrir que mientras modificamos un dato que esta mutando otro proceso puede hacer uso de el. Debemos tener cuidado con esta característica. Puedes pensar en bloquear un grano… Los granos nos permiten ejecutar tareas bloqueantes y por tanto esto hacer el trabajo, no sin el problema del pool de hilos. Lo normal no es bloquear granos y que sean tareas no mutables. Los granos permiten workers sin estado, que nos permite trabajo recurrentes sin estar ligado a un hilo. Y los granos permiten filtros que nos permiten interceptar llamadas, permitiendo usar telemetría que nos sirve para saber el estado antes y después de un grano. Existen filtros entrantes y salientes.
una explicación directa de un silo: lugar seco en donde se guarda el trigo u otros granos, semillas o forraje. Un silo puede mantener varios tipos de granos y puede unirse a otros silos para formar un cluster. Los silos tambien desactivan los granos después de un determinado tiempo de inactividad. Los silos permiten instanciar una ingente cantidad e granos sin tener que escribir código personalizado en ciclo de vida. Tenemos que preocuparnos, por los umbrales de escaldo: es decir, probar la carga en una instancia individual, calcular rendimientos y por tanto extrapolar. El ciclo de vida de un silo, imagen anterior, muestra los pasos que sigue un silo desde que se inicia, hasta que funciona o se apaga. El apagado se inicia con la eliminación de un silo. Como ya hemos dicho en comparación con Akka la configuración es algo trivial. Vuelvo a repetir que es un ahorro directo si comparamos con Akka. Silo – Lugar donde se almacenan los granos
el estado actual de un grano, como por ejemplo si esta activado. Los granos pasan continuamente de un estado a otro, y las cargas pueden requerir el uso de un grano activo. Cada grano tiene un identificar único, como por ejemplo: Nombre-Grano-001 o Nombre-Grano-002, … que realizan trabajos diferentes en función de la ingesta, aunque sean del mismo tipo de granos. En caso de que una carga solicite Nombre-Grano-001, el silo consulta el Directorio de Granos para determinar si el grano esta activo y determinar el servidor de referencia. Si el grano no esta activo, el silo creará un grano y luego le enviará la carga de trabajo. Si el grano existe, se enviará al silo que lo posee. El silo remitente recibirá una respuesta si es necesario, de que pasa el cliente. Para esto necesitaremos un NuGet llamado Microsoft.Orleans.Server, que lo que hace es tener una tabla hash distribuida en memoria. Usa las opciones por defecto, aunque podrás usar Redis y Azure Storage.
varios elementos durante su flujo de trabajo. El silo es el tiempo de ejecución (runtime) para los granos y mensajes entre los granos y cliente. Por ejemplo: 1. Un cliente crea una solicitud y la envía al silo. 2. El silo recibe la petición y asocia la llamada al grano. 3. El grano 1 recibe el mensaje y si el grano esta ocupado, los mensaje se encolan, para usar el modelo de concurrencia basado en turnos que ejecuta subtareas en el mismo hilo. 4. El grano 2 recibe un mensaje para consultar información. El grano 2 recopila la información y mapeo de los objetos que luego envía al grano 3. 5. E grano 3 formatea el mensaje en el formato de respuesta y lo devuelve al grano 2. 6. El grano 2 devuelve la información al grano 1. 7. La respuesta llega al cliente.
que pueden comunicarse entre sí. Los silos tambien controlan la conectividad entre ellos. Lo que se consigue es que si un silo falla y una vez detectado el fallo, los granos pueden ser reinstalados en otro silo que soporte el mismo tipo de granos. Suponiendo que haya otro silo que soporte el tipo de granos que se ejecuta, los clientes no serán conscientes del fallo, a parte de la posible latencia. Afiliación de un silo La pertenencia a un silo permite que los silos se conozcan entre sí y se unan para forma un cluster. Esto incluye la posibilidad de que los silos determinen la salud de otros silos y la compartan dentro del cluster. Según Microsoft, todas las ejecuciones son capaces de pasar por igual a través de los silos. Lo logra gracias a una tabla centralizada que cada silo, dentro del cluster, utiliza para determinar la salud de otros silos. Básicamente, lo que ocurre es que lo silos hacen ping directamente para determinar si estan vivos. Supongo que esta técnica te sonará. El ping hace que se cree un anillo virtual de silos. Esto se llama hashing consistente. Permite que los silos sean retirados del cluster y añadidos al mismo, de modo que el cluster en su conjunto no se ve afectado. Cluster – Agrupación de silos
multi-cluster que muestra la interconexión entre silos y cluster que refuerza la explicación anterior de la interconexión de la tabla hash. Se basa en un protocolo llamado: Gossip Protocol. Es un proceso de comunicación entre pares y se usa en sistemas distribuidos. La forma en que funcionan los silos se emparejan al azar y comunican información sobre otros silos. Este protocolo necesita un estudio más calmado, por eso, te dejo un enlace para que puedas estudiarlo. Registro de Granos Los granos registrados pueden recibir notificaciones cuando se detecta un error de conexión y tambien cuando se ha resuelto. Es posible que el intercambio entre cluster no se sincronice inmediatamente ya que habrá diferencias en el estado a medida que se actualicen los valores debido a la consistencia eventual (replicación de datos enter aplicaciones).
en nuestro caso un estado, comparte eventualmente el último estado actualizado. Si en varios nodos se almacena el mismo valor, y se actualiza en otro nodo, el último valor actualizará el resto de nodos. En algún momento todos los nodos tendrán el mismo valor: en nuestro caso el cluster/silos. Esto quiere decir que alguno silos tendrá la información antes que otros. Silos Heterogéneos Es decir mezclan diferentes tipos, esto es un problema por si en un cluster un silo abandona el cluster, se producirá un problema de runtime, ya que era el único silo que tenía ese tipo de grano, y debido a la consistencia eventual no todo se informa instantánea. Por tanto, lo mejor es mantener varios tipos de granos con redundancia en silos, por si ocurre algún error. Esto tiene algunas limitaciones con los Worker grains, cuidado con esto.
son tu enemigo”, no todas las aplicaciones han de ser descompuesta en microservicios ya que el crecimiento futuro y el coste no vale la pena. Voy a mostrar pinceladas de pros y contras en monolitos y microservicios para ver como Orleans nos puede ayudar en ambos casos. Como bien sabéis los monolitos son más fácil de depurar, por ejemplo. Por eso se siguen usando, se usan desde hace décadas. Pero tanto tiene cosas buenas como malas: por ejemplo, malo, los despliegues debido a su gran acoplamiento. Los microservicios, son la moda, si no está haciendo microservicios, parece que actualmente un desarrollador de segunda. Pero como decía: tiene cosas buenas y malas. Permiten escalar y desplegar sin el acoplamiento, pero depurar un problema puede suponer un gran esfuerzo. Tanto en una como otra, Orleans necesita implementar interfaces al añadir granos. Esto obliga a un diseño un poco acoplado. Esto puede ser visto como un megalito que puede albergar una gran cantidad de servicios más pequeños; sin embargo los granos requieren menos esfuerzo que los microservicios ya que no requieren desarrolladores que configuren los enrutamientos, escalados. Etc. Un modelo de actor con Orleans, prescinde de complejidad. Pero como este modelo es poco conocido, os tengo que advertir que la implementación en monolito o microservicio difiere. Pero no os preocupes, gracias a como está implementado ya tenga conocimiento de una u otra o ambas arquitecturas podrás ver claramente su implementación. Es una herramienta – Debemos determinar si se ajusta a nuestro problema
tienes sistemas con una gran cantidad de entidades. Cuando tienes sistemas estrechamente acoplados que no tienen perspectiva de sistema distribuido. Cuando tienes sistemas poco conectados y almacenamiento en cache inteligente. Cuando tienes pocos componentes y objectos Cuando te ves obligado a ejecutar una aplicación en más de 1 servidor (durante horas o picos, como un Black Friday) Cuando tienes la intención de realizar tareas de informática defensiva. Cuando tu aplicación necesita tiempo de inactividad cero. Cuando tienes acores que necesitan acceso directo a memoria. Cuando tu cliente necesita respuesta rápidas. Cuando es necesaria una cooperación global para el funcionamiento de la aplicación. Cuando tienes aplicaciones que tienen varios actores con una larga vida útil (productos, inventarios, etc.) o cortas (transacciones de pago, pedidos, compras, etc.) Cuando administras múltiples versiones de granos es muy complejo y se vuelve inmanejable. Y te obliga a mantener granos con estado A o estado B.
proyectos: • 2 proyectos para cliente, 1 para silo y 1 para cliente. • 2 proyectos para librerías, 1 para granos y 1 para interface de granos. Para ello necesitaremos Visual Studio 2022 ya que vamos a trabajar con .NET6. Os podéis descarga la siguiente solución: Ya era hora – Tal y como nos gusta, llega el momento de tirarse al barro https://github.com/jmfloreszazo/HandsOnIoTAzure
proyectos: • 2 proyectos para cliente, 1 para silo y 1 para cliente. • 2 proyectos para librerías, 1 para granos y 1 para interface de granos. Para ello necesitaremos Visual Studio 2022 ya que vamos a trabajar con .NET6. Os podéis descarga la siguiente solución: Desgranando el proyecto – Vamos paso a paso
entre proyectos y librerías utilizadas. Para que sirve cada NuGet: • Microsoft.Extensions.Logging.Console – Logs para la consola. • Microsoft.Extensions.Logging.Abstractions – Se usa para loggear y los niveles de log. • Microsoft.Orleans.Client – Librerias que se usan por el cliente para comunicarse con Orleans. • Microsoft.Orleans.Server – Una colección de librerías para hosting. • Microsoft.Orleans.CodeGenerator.MSBuild – Usa Roslyn para generar codigo en tiempo de ejecución, se usa para análisis via reflection. • Microsoft.Orleans.Core.Abstractions – Una seri de abstracciones que incluye desde errores, interfaces, etc. Así mismo en la anterior captura podéis ver la relación que existen entre dependencias del proyecto. Este ejemplo que os muestro esta basado en el siguiente ejemplo: https://dotnet.github.io/orleans/docs/tutorials_and_samples/tutorial_1.html Lo mejor es que ejecutes el proyecto, recuerda que tienes que lanzar multiples proyectos: silo y client, para que se pueda ver el funcionamiento.
cliente llama con un Id de Grano y si esta activo (o en otro estado) o necesita crearlo. Si el grano esta activo, el mensaje se envía y se devuelve una respuesta. Si no esta instanciado, lo debe activar y luego realiza el trabajo. Todo esto ocurre en muy poco tiempo, te nen cuenta que se autogestiona el ciclo de vida y sobre todo la memora. Client Silo Grain 1 Grain 2 Activo Activo
grano? El ejemplo anterior era sobe como un cliente comunica con un grano, pero ya hemos dicho que se pueden comunicar granos con otros granos. Para ello vamos a cargar la rama GrainCommunications, en la rama main de Git tenéis el anterior ejemplo y en esta rama un test para que se vea esta comunicación interna. Client Silo Grain 1 Calling TestSample
ejemplo, para saber la ultima información, en los casos de uso tenemos algo claro: obtención de las fluctuaciones del mercado de valores. En Orleans tenemos temporizadores y recordatorios para llevar a cabo este tipo de implementaciones. Estos se pueden configurar a intervalos de tiempo específicos. La vida útil de un temporizador no persiste más allá de la vida útil del grano o si existe un fallo. Valen para actualizar feeds, marcadores y acciones. Son desechables, podemos apagarlos cuando queramos y así liberar recursos. En resumen: no sobreviven a la vida del grano o cluster. Mientras que los temporizadores se usan para períodos de tiempo más cortos y que se ejecuten a intervalos pequeños, como minutos o segundos. ¿Qué pasa si necesitamos que una aplicación se lleve a cabo incluso existiendo un falla? Los recordatorios se almacenan en una base de datos y continúan una vez que se reinician. Los recordatorios viven fuera de la vida útil cluster, por tanto deberán ser desactivados. Dado que son persistentes, se volverán a ver incluso creando una nueva instancia de un cluster fallido. Los recordatorios estan diseñando para ejecutarse con menos frecuencia frente a los temporizadores que son más mayor frecuencia. Llamadas recurrentes – Veamos como…
uso o tras un fallo. Esto nos permite que no tengamos que mantenerlos. Vistas las diferencias, ya sabemos aplicar el que corresponda para el requisito de nuestra aplicación. Una base de implementación típica es que los recordatorios se pueden usar para orquestar temporizadores. Veamos un pequeño ejemplo: para ello vamos a cargar la rama TimerSample. Timer – Otra rama del proyecto
Ya que necesitamos persistencia y vamos a usar Azure Storage Tables. Para ello vamos a cargar la rama ReminderSample. Lanza los siguientes comandos en Azure CLI para crear los recursos necesarios: az group create -l westeurope -n MyResourceGroup az storage account create -n myorleansdemosta -g MyResourceGroup -l westeurope --sku Standard_LRS az storage table create --name sampleorleanstbl --account-name myorleansdemosta Reminders – Otra rama del proyecto
para que veáis que tambien se puede testear la aplicación. Para ello vamos a usar xUnit. Veamos un pequeño ejemplo: para ello vamos a cargar la rama xUnitTestSample. Que está basado en la documentación: https://dotnet.github.io/orleans/docs/tutorials_and_samples/testing.html y donde podréis ver bien la configuración del silo que ya he dejado preparara para la última versión de Orleans. Como viene siendo habitual, puedes usar Fluent y Moq, sin ningun problema. Testing – ¿Quién no hace testing?
blog podéis encontrar una entrada con un ejemplo donde os muestro el ejemplo con Darp: https://jmfloreszazo.com/net-6-dapr-modelo-de-actores/ Elimina el Lock-Vendor – Como usar el modelo basado en Orleans con Dapr
Insight en un proyecto, solo es necesario una vez creada la infraestructura y con el Id de Insight, añadir esta instrucción: • Primero añadir el NuGet: Microsoft.ApplicationInsights.AspNetCore • Añadir el appsettings.json, con • "Telemetry": { "InstrumentationKey": "Your_Instrumentation_Key" } • Y añadir: services.AddApplicationInsightsTelemetry("Your_Instrumentation_Key"); Como ves es exactamente igual que cualquier aplicación Asp Net. Nada más que añadir. ¿Qué tengo que hacer? – Con esta instrucción ya tienes la base
un mensaje para actualizar un dato, actualmente lo estamos haciendo en memoria. ¿Qué sucede si el grano se desactiva? El estado interno de la memoria se desactiva y por tanto el dato se pierde, es decir estamos trabajando en stateless. Lógicamente nos interesa en muchos casos guardar el estado del grano en una base de datos, como Azure Cosmos DB, Storages (ver ejemplo anterior donde usábamos en memoria). Con un estado permanente el grano puede cargar el estado desde el almacenamiento de persistencia durante la activación. Orleans permite varios sistema de persistencia: CosmosDB, Azure Storage Table y Amazon DynamoDB. Por supuesto con otras bases de datos relacionales como SQL Server y Oracle via ADO. Por ejemplo para ComosDB: builder.Host.UseOrleans(siloBuilder => { siloBuilder.UseLocalhostClustering() .AddCosmosDBGrainStorageAsDefault(opt => { opt.AccountEndpoint = "<<Cosmos DB accoutn Endpoint>>"; opt.AccountKey = "<<Cosmos DB account Key>>"; opt.DB = "<<Cosmos DB Database name>>"; opt.CanCreateResources = true; }) Statefull – Los granos pueden ser stateless o statefull
voy a entrar en detalles de este patrón que se usa mucho en sistemas distribuidos y arquitectura de microservicios. Orleans soporta nativamente Pub-Sub implementando Orleans Streams. Al igual que los Actores, los Stream Orleans son virtuales. Es decir funcionan de forma asimétrica dentro de un silo usando un grano o fuera de un silo usando como Cliente de Orleans. Para añadir información: https://github.com/dotnet/orleans#streams Por ejemplo existen otras cosas que dejo para vuestro propio estudio: • Para versionado: https://github.com/dotnet/orleans#grain-versioning--heterogeneous-clusters • Elasticidad, …: https://github.com/dotnet/orleans#elastic-scalability--fault-tolerance Y más – Cosas que puede hacer además Orleans.
configurado, testeado, llamado entre granos, etc. ¿Pero como vamos a supervisar los cluster en tiempo real?. Ya os he mostrado que podemos poner logs y usar Azure Application Insight, pero como se que muchas personas no tendrán acceso a Azure o bien no quieren usar un recurso externo. La respuesta es el panel de control de Orleans. Esta herramienta se usa para ayudarnos durante el desarrollo. Nos permite monitorizar cosas tales como: numero de peticiones, fallos y tiempos de respuesta. Es muy sencillo de usar. Tenemos que: Para desarrolladores – Más que para producción
dashboard en el builder del silo. Arrancamos la aplicación y entramos en la siguiente dirección: http://127.0.0.1:8080/ Donde podremos ver el panel de control.
tienes tanto las referencias como el código para que puedas ver como funciona. Añadimos referencias y dejamos para ejecutar el proyecto del API para poder probar el silo y el cliente: Creamos API con Orleans – Vamos a ver el ejemplo paso a paso
AKS para subir nuestra imagen. Y para poder desplegar seguir los pasos de este tutorial: https://docs.microsoft.com/es-es/dotnet/architecture/containerized-lifecycle/design-develop-containerized-apps/build- aspnet-core-applications-linux-containers-aks-kubernetes Lo más importante es que veáis que debemos hacer uso de:
plan de CI/CD, no difiere en nada a lo que ya supongo conocéis, si no es así. Os dejo enlaces: • Azure DevOps: https://azure.microsoft.com/en-us/services/devops/ • Jenkins: https://www.jenkins.io/doc/tutorials/ • GitHub: https://jmfloreszazo.com/github-actions/ donde podrás obtener un monográfico muy completo. Con todo esto y vuestro Git asociado ya podéis cerrar el ciclo de DevOps. Por supuesto – Ya habéis visto el deploy en dos sitios