Se necesita implementar una API que permita a través de peticiones HTTP retornar la cantidad de decimales de número PI (π), si este no se encuentra en una cache, se debe hacer el cálculo, de lo contrario, la cache debe devolver el registro. Es por esto que se realiza una investigación para encontrar la forma más eficiente para realizar este cálculo de decimales.
Existen varios métodos por los cuales se pueden calcular de forma aproximada los decimales de PI (π) y una de las más eficientes es a través de La fórmula de BPP, por lo que es la indicada para el desarrollo del proyecto.
El proyecto va a contener dos microservicios el primero será una API desarrollada en Java 11, bajo el alero de Springboot y una cache Distribuida de Redis. Ambos estarán dockerizadas y se iniciarán por medio del archivo docker-compose.yml
La solución fue desarrollada con la siguiente tecnología:
-
Java 11 -> Como lenguaje de programación
-
SpringBoot -> Como framework de Java, el cual permite la creación de una API
-
Redis -> Como memoria caché distribuida.
-
Dockers -> Para el levantamiento y funcionamiento de la aplicación
Plugins relevantes de Springboot:
-
Maven -> Como Configuración del proyecto en Springboot.
-
Junit -> Para pruebas unitarias.
-
Jacoco -> Para reportes de test.
-
Swagger -> Para vizualizacion de endpoint y documentación del contrato de la aplicación.
Software externos:
-
Postman -> Para probar el servicio a traves de llamadas http
-
K6-> Para pruebas de carga
-
La colección de postman de encuentra en la raiz del proyecto, al igual que el makefile, DockerFile y docker-compose.yml
-
El algoritmo empleado es uno de los más certeros, que puede variar al momento de aproximar en el último dígito del decimal calculado.
-
Para efectos de programa, el número mínimo que puede ingresar el usuario es 1.
-
La configuración Redis es la por default, por lo que no posee usuario ni contraseña.
-
Para entrar a swagger, dirigirse a link
El proyecto debe estar levantado para poder ver swagger
- Para vizualizar porcentaje de coverage entregado por Jacoco, desde la carpeta raiz del proyecto dirigirse a ./target/site/jacoco/index.html y luego abrir el archivo index.html en un browser.
El proyecto debe estar levantado para poder ver el porcentaje
A continuación se presenta la estructura del directorio principal, el cual sería src.
├───main
│ ├───java
│ │ └───com
│ │ └───example
│ │ └───meli
│ │ ├───adapters
│ │ │ └───controllers
│ │ ├───commons
│ │ │ ├───constants
│ │ │ ├───dto
│ │ │ ├───exception
│ │ │ ├───utils
│ │ │ └───validator
│ │ ├───config
│ │ └───domain
│ │ ├───models
│ │ └───services
│ └───resources
└───test
└───java
└───com
└───example
└───meli
├───commons
│ └───utils
├───controller
└───domain
├───model
└───services
El proyecto se divide en 4 carpetas principales:
-
Adapters: Aquí se encontrará los controladores de la aplicación.
-
Commons: Se encuentran elementos que permitiran y ayudaran de manera transversala la aplicación a realizar sus tareas, dentro de estas tenemos Constantes, Dtos de Respuesta, Excepciones, Validadores y Utils.
-
Config: Se encuentran configuraciones del proyecto, como por ejemplo la configuración de Swagger
-
Domain: Se encuentran tanto los modelos como los servicios construidos.
Para vizualizar el diagrama de arquitectura, dirigirse a este link
Para vizualizar el diagrama de flujo de todos los flujos, lo puedes ver en el siguiente link
Flujo Get Pi NO Random
Flujo Delete from cache
La url Base del proyecto iniciado es: http://localhost:8089/api/v1/
DockerFile de contenerdor de API
FROM openjdk:11-jre-slim-buster
EXPOSE 8089
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
docker-compose.yml para orquestar el contenedor de Redis y Api
version: '3'
services:
backend:
environment:
- MAX_RANDOM_PRECISION=25
- REDIS_ENABLED=1
build: .
ports:
- "8089:8089"
links:
- redis
redis:
image: redis
container_name: redis
ports:
- 6379:6379
Los puertos de nuestra aplicación para la API es en 8089 y para Redis 6379
Las variables de entorno MAX_RANDOM_PRECISION y REDIS_ENABLED se encuentran configurados en la sección environment de nuestro docker-compose.yml , estos están inicialmente seteados en 25 y 1 respectivamente.
De querer actualizar, cambiar los valores en docker-compose y levantar nuevamente los servicios.
docker compose up -d --build
En la raiz del repositorio, ejecutar el comando para poder generar el .jar del aplicativo, este será generado con el Maven embebido.
./mvnw clean install
Luego, para levantar los ambientes en docker, realizar siguiente comando
docker compose up -d --build
En la raiz del proyecto, correr el siguiente comando
make --file makefile.mk compile docker-run
o
make --file makefile.mk run-project
MakeFile construido para levantamiento
default:
cat ./makefile.mk
compile:
./mvnw clean install
docker-run:
docker compose up -d --build
run-project:
compile docker-run
En caso de tener cualquier problema con el makefile, levantar el proyecto de forma manual.
Se ha dejado un script con pruebas de carga de las tres funciones requeridas. Para correr los test de carga:
Ingresar desde la raiz del proyecto a la carpeta k6 y ejecutar el siguiente comando:
Desde powershell de Windows:
cat test.js | docker run --network host --rm -i grafana/k6 run -
Desde consola Unix:
docker run --network host --rm -i grafana/k6 run -<test.js
Para que las pruebas funcionen, la aplicación debe estar corriendo
Las pruebas fueron probadas en ambiente Windows
Ejemplo de salida tras pasar Test
default ✓ [ 100% ] 0/2 VUs 22s
✗ is status 409
↳ 0% — ✓ 143 / ✗ 16265
expuest ✓ is status 200
checks.........................: 66.95% ✓ 32959 ✗ 16265
data_received..................: 14 MB 627 kB/s
data_sent......................: 5.5 MB 248 kB/s
http_req_blocked...............: avg=2.51µs min=641ns med=1.02µs max=1.11ms p(90)=1.38µs p(95)=1.8µs
http_req_connecting............: avg=946ns min=0s med=0s max=1.04ms p(90)=0s p(95)=0s
http_req_duration..............: avg=571.68µs min=270.17µs med=475.42µs max=9.92ms p(90)=860.71µs p(95)=904.64µs
{ expected_response:true }...: avg=572.03µs min=270.17µs med=475.68µs max=9.92ms p(90)=860.81µs p(95)=904.68µs
http_req_failed................: 0.29% ✓ 143 ✗ 49081
http_req_receiving.............: avg=28.25µs min=-84295ns med=26.03µs max=748.38µs p(90)=40.91µs p(95)=47.82µs
http_req_sending...............: avg=5.69µs min=3.09µs med=4.63µs max=663.12µs p(90)=6.42µs p(95)=9.66µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=537.73µs min=222.63µs med=442.55µs max=9.79ms p(90)=826.7µs p(95)=866.02µs
http_reqs......................: 49224 2237.354448/s
iteration_duration.............: avg=1.85ms min=1.46ms med=1.79ms max=12.32ms p(90)=2ms p(95)=2.15ms
iterations.....................: 16408 745.784816/s
vus............................: 1 min=1 max=2
vus_max........................: 2 min=2 max=2
Si su API la quisiéramos exponer de manera pública:
-
¿Qué componentes usarías para securitizar tu API?.
Los componentes a utilizar serían restringir los accesos a la API mediante JWT a través de su autenticador y autorizador o a través de una APIKEY.
-
¿Cómo asegurarías tu API desde el ciclo de vida de desarrollo?
La forma sería que en cada construcción de componentes nuevos dentro de la APi, poder generar pruebas unitarias, tambien se puede generar un pipeline de CI/CD para verificar y corroborar variables y secretos expuestos(hardcodeados) en el código. Esto con el fin de que en cada incremento de la aplicación se pueda mantener las buenas prácticas y validaciones, tanto de pruebas unitarias como de vulnerabilidades de codigo.
Por último cambiar los puertos por defectos que ofrecen los diferentes servicios (DB, APIS, etc)