0

Microsoft Orleans #20: Caso Práctico – Construcción de un Sistema Distribuido

En esta última clase aplicaremos todo lo aprendido para construir un sistema distribuido completo con Orleans. Implementaremos un sistema de gestión de pedidos en línea, asegurando escalabilidad, persistencia y seguridad.

Objetivos de la Clase

  • Construir una arquitectura distribuida basada en Orleans.
  • Implementar Granos para gestionar usuarios, pedidos y pagos.
  • Utilizar persistencia con SQL Server para almacenar el estado.
  • Exponer Orleans como API REST con ASP.NET Core.
  • Asegurar alta disponibilidad y escalabilidad con Orleans.

Arquitectura del Sistema

Componentes principales

Usuarios → Registran y consultan información sobre sus cuentas.
Pedidos → Un usuario puede hacer múltiples pedidos.
Pagos → Cada pedido requiere un pago procesado.

Diagrama del Flujo del Sistema

  1. Un usuario crea un pedido.
  2. El sistema asigna un número de pedido único.
  3. Se procesa el pago de forma asincrónica.
  4. Cuando el pago se confirma, el pedido cambia a estado «completado».
  5. El usuario puede consultar el historial de pedidos en cualquier momento.

Implementar los Granos del Sistema

Interfaz del Grano de Usuario

using System.Threading.Tasks;
using Orleans;

public interface IUsuarioGrain : IGrainWithStringKey
{
    Task RegistrarUsuario(string nombre, string email);
    Task<string> ObtenerInformacion();
}

Implementación del Grano de Usuario

using System.Threading.Tasks;
using Orleans;

public class UsuarioGrain : Grain, IUsuarioGrain
{
    private string _nombre;
    private string _email;

    public Task RegistrarUsuario(string nombre, string email)
    {
        _nombre = nombre;
        _email = email;
        return Task.CompletedTask;
    }

    public Task<string> ObtenerInformacion()
    {
        return Task.FromResult($"Usuario: {_nombre}, Email: {_email}");
    }
}

Este Grano almacena información básica del usuario.

Interfaz del Grano de Pedido

using System.Threading.Tasks;
using Orleans;

public interface IPedidoGrain : IGrainWithGuidKey
{
    Task CrearPedido(string usuarioId, decimal monto);
    Task ConfirmarPago();
    Task<string> ObtenerEstado();
}

Implementación del Grano de Pedido

using System.Threading.Tasks;
using Orleans;

public class PedidoGrain : Grain, IPedidoGrain
{
    private string _usuarioId;
    private decimal _monto;
    private bool _pagado = false;

    public Task CrearPedido(string usuarioId, decimal monto)
    {
        _usuarioId = usuarioId;
        _monto = monto;
        return Task.CompletedTask;
    }

    public Task ConfirmarPago()
    {
        _pagado = true;
        return Task.CompletedTask;
    }

    public Task<string> ObtenerEstado()
    {
        return Task.FromResult(_pagado ? "Completado" : "Pendiente de pago");
    }
}

Este Grano representa un pedido y su estado de pago.

Interfaz del Grano de Pagos

using System.Threading.Tasks;
using Orleans;

public interface IPagoGrain : IGrainWithGuidKey
{
    Task<bool> ProcesarPago(string pedidoId, decimal monto);
}

Implementación del Grano de Pagos

using System;
using System.Threading.Tasks;
using Orleans;

public class PagoGrain : Grain, IPagoGrain
{
    public async Task<bool> ProcesarPago(string pedidoId, decimal monto)
    {
        await Task.Delay(2000); // Simular procesamiento de pago
        Console.WriteLine($"Pago de {monto:C} procesado para el pedido {pedidoId}");
        return true;
    }
}

Este Grano simula el procesamiento de un pago.

Configurar Persistencia en SQL Server

Agregar Configuración en Program.cs

siloBuilder.AddAdoNetGrainStorage("pedidoStore", options =>
{
    options.Invariant = "System.Data.SqlClient";
    options.ConnectionString = "Server=localhost;Database=OrleansPedidos;User Id=sa;Password=YourPassword;";
});

Esto permite que los pedidos persistan en la base de datos.

Exponer Orleans como API REST

Crear PedidoController.cs

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Orleans;

[ApiController]
[Route("api/pedidos")]
public class PedidoController : ControllerBase
{
    private readonly IGrainFactory _grainFactory;

    public PedidoController(IGrainFactory grainFactory)
    {
        _grainFactory = grainFactory;
    }

    [HttpPost("{usuarioId}/crear")]
    public async Task<IActionResult> CrearPedido(string usuarioId, [FromBody] decimal monto)
    {
        var pedidoGrain = _grainFactory.GetGrain<IPedidoGrain>(Guid.NewGuid());
        await pedidoGrain.CrearPedido(usuarioId, monto);
        return Ok($"Pedido creado con éxito para usuario {usuarioId}");
    }

    [HttpPost("{pedidoId}/pagar")]
    public async Task<IActionResult> PagarPedido(Guid pedidoId)
    {
        var pagoGrain = _grainFactory.GetGrain<IPagoGrain>(pedidoId);
        bool pagoExitoso = await pagoGrain.ProcesarPago(pedidoId.ToString(), 100);
        if (pagoExitoso)
        {
            var pedidoGrain = _grainFactory.GetGrain<IPedidoGrain>(pedidoId);
            await pedidoGrain.ConfirmarPago();
            return Ok("Pago procesado con éxito.");
        }
        return BadRequest("Error en el pago.");
    }

    [HttpGet("{pedidoId}/estado")]
    public async Task<IActionResult> ObtenerEstado(Guid pedidoId)
    {
        var pedidoGrain = _grainFactory.GetGrain<IPedidoGrain>(pedidoId);
        string estado = await pedidoGrain.ObtenerEstado();
        return Ok($"Estado del pedido: {estado}");
    }
}

Esto permite interactuar con Orleans usando HTTP.

Probar la Aplicación

Ejecutar Orleans y la API

dotnet run

Crear un Pedido

curl -X POST "http://localhost:5000/api/pedidos/user123/crear" -H "Content-Type: application/json" -d "100"

Salida esperada:

Pedido creado con éxito para usuario user123

Pagar un Pedido

curl -X POST "http://localhost:5000/api/pedidos/{pedidoId}/pagar"

Salida esperada:

Pago procesado con éxito.

Consultar Estado del Pedido

curl -X GET "http://localhost:5000/api/pedidos/{pedidoId}/estado"

Salida esperada:

Estado del pedido: Completado

Cuestionario de Autoevaluación

  1. ¿Cómo se estructuró el sistema de gestión de pedidos en Orleans?
  2. ¿Cómo Orleans maneja la persistencia de pedidos en SQL Server?
  3. ¿Cómo interactúan los Granos de Pedidos y Pagos en la API REST?
  4. ¿Qué ventajas ofrece Orleans en comparación con un sistema tradicional basado en bases de datos centralizadas?
  5. ¿Cómo Orleans asegura escalabilidad y concurrencia en este sistema?

Resumen de la Clase

  • Construimos un sistema distribuido completo con Orleans.
  • Usamos Granos para Usuarios, Pedidos y Pagos.
  • Persistimos datos en SQL Server para garantizar durabilidad.
  • Exponemos Orleans como API REST con ASP.NET Core.

Conclusiones y Agradecimientos

Después de recorrer 20 clases, hemos explorado todos los aspectos esenciales de Microsoft Orleans, desde los fundamentos hasta su aplicación en sistemas distribuidos escalables y de alta disponibilidad. Orleans nos ha demostrado ser una tecnología poderosa y flexible para construir aplicaciones distribuidas, optimizando la gestión de concurrencia, persistencia y escalabilidad de una manera sencilla y eficiente.

¿Qué hemos aprendido?

Conceptos Fundamentales de Orleans

  • Cómo Orleans implementa el Actor Model para gestionar concurrencia y estado de manera eficiente.
  • Activación y ciclo de vida de los Granos en un sistema distribuido.

Orleans en Producción

  • Persistencia de Granos en bases de datos SQL Server y MongoDB.
  • Orleans Streams para comunicación eficiente entre Granos.
  • Seguridad con autenticación JWT y autorización en Orleans.

Optimización y Escalabilidad

  • Mejoramos la performance ajustando activación de Granos, uso de Streams y balanceo de carga.
  • Alta disponibilidad con múltiples nodos Orleans, asegurando redundancia y resiliencia.
  • Uso de Kubernetes y Docker para entornos de producción escalables.

Caso Práctico Completo

  • Implementamos un sistema distribuido real usando Orleans con API REST en ASP.NET Core.
  • Aplicamos persistencia, autenticación, transacciones y escalabilidad en un entorno Orleans de producción.

Agradecimientos Especiales

¡Gracias por seguir este curso y sumergirte en el mundo de Orleans!

Este no es el final, sino el comienzo de tu viaje en sistemas distribuidos. Con Orleans, ahora tienes el conocimiento y las herramientas para desarrollar aplicaciones altamente escalables y resilientes.

Fernando Sonego

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *