Lo siguiente que estuve investigando, cuando empecé con Dapr, fue cómo era posible llevar a cabo la comunicación entre APIs que usaran esta tecnología. Como ya comenté en el primer artículo, uno de los bloques contemplados es Service-to-service invocation y es lo que vamos a ver hoy.
¿Por qué me interesa usar Dapr entre APIs?
Lo primero que te puedes preguntar es … ¿y para qué me sirve usar Dapr en este caso? Pues los motivos principales son los siguientes:
- Te ayuda con la lógica de reintentos (puedes encontrar más información en este enlace).
- Las llamadas entre servicios se protegen con autenticación mutua (mTLS), incluida la sustitución automática de certificados.
- Se puede controlar qué operaciones pueden hacer los clientes de esas APIs mediante directivas de control de acceso.
- Se pueden capturar trazas y métricas de todas las llamadas entre servicios para proporcionar información y diagnóstico.
¿Te he convencido? 😃 Pues vamos con la demostración.
Aplicación de ejemplo
Para poder entender cómo funciona esta comunicación entre servicios, he vuelto a tomar como ejemplo mi API Tour of heroes 🦸🏻♀️ y he creado una nueva llamada Tour of villains 🦹♀️. Esta segunda será la que sea llamada por la primera y que devolverá el villano que le corresponde a cada héroe, por tener algo con lo que interactuar:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using tour_of_villains_api.Models;
namespace tour_of_villains_api.Controllers;
[ApiController]
[Route("[controller]")]
public class VillainController : ControllerBase
{
private VillainContext _context;
public VillainController(VillainContext context)
{
_context = context;
}
// GET: /villain
[HttpGet("{heroName}")]
public Villain GetVillain(string heroName)
{
return _context.Villains
.Include(v => v.Hero)
.Where<Villain>(v => v.Hero.Name == heroName)
.SingleOrDefault();
}
// POST: /villain
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<Villain>> PostHero(Villain villain)
{
_context.Villains.Add(villain);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetVillain), new { heroName = villain.Hero }, villain);
}
}
Como ves, esta es muy parecida a la anterior. Tiene un método get, que recibe como parámetro el nombre del héroe para buscarlo en su base de datos de villanos y un método post para poder dar de alta algunos datos, haciendo uso del archivo http.client que tengo como parte del repo.
¿Cómo se produce la comunicación entre los servicios?
Cuando estás trabajando con Dapr lo primero que siempre que debes tener en cuenta es que la comunicación entre las partes se hace siempre a través de la API de Dapr y nunca directamente, y eso también aplica en el caso de los servicios. Por ello, cuando queremos invocar a otro servicio, pediremos a nuestro Dapr que resuelva el nombre del otro servicio con el que nos queremos comunicar y así poder hacer la llamada a su método.
Si trasladamos esto a nuestra aplicación de ejemplo, la llamada desde Tour Of Heroes API sería como la siguiente:
//Service-to-service invocation
// GET: api/hero/villain/{heroName}
[HttpGet("villain/{heroName}")]
public async Task<Villain> GetVillain(string heroName)
{
_logger.LogInformation($"Finding the villain for {heroName}...");
Villain? villain = null;
try
{
villain = await _daprClient.InvokeMethodAsync<Villain>(
HttpMethod.Get,
"tour-of-villains-api",
$"/villain/{heroName}"
);
}
catch (InvocationException ex)
{
_logger.LogError(ex.Message);
}
return villain;
}
A través de DaprClient podemos utilizar el método InvokeMethodAsync, donde le pasamos el verbo a utilizar, el nombre del servicio que queremos invocar y cuál es la ruta dentro de este.
Ejecutar el ejemplo
Para ejecutar este ejemplo, puedes hacerlo con la configuración que acompaña al código de ambos proyectos con Visual Studio Code y pulsando F5 en cada una de las APIs. También puedes lanzar desde el terminal el comando que corresponde a cada proyecto:
Para Tour Of Heroes API:
dapr run --app-id tour-of-heroes-api --app-port 5222 -- dotnet run
Para Tour Of Villains API:
dapr run --app-id tour-of-villains-api --app-port 5111 --dapr-http-port 3501 -- dotnet run
Si queremos invocar la llamada para recuperar al villano desde la API de los héroes podemos acceder desde el navegador/Postman/cURL a esta dirección: http://localhost:5222/api/hero/villain/batman y nos devolverá el Joker.
¡Saludos!