En esta sección, vamos a llevar a cabo una implementación completa de un sistema basado en microservicios. Este ejercicio práctico te permitirá aplicar los conceptos y técnicas aprendidas en los módulos anteriores. Vamos a construir una aplicación sencilla de gestión de pedidos que incluye varios microservicios.
Objetivos del Ejemplo Práctico
- Diseñar y definir los microservicios necesarios.
- Implementar los microservicios utilizando una tecnología de tu elección (por ejemplo, Node.js, Spring Boot, etc.).
- Configurar la comunicación entre los microservicios.
- Desplegar los microservicios utilizando contenedores Docker.
- Orquestar los microservicios con Kubernetes.
- Implementar monitoreo y logging.
- Asegurar los microservicios.
- Diseño y Definición de Microservicios
Microservicios Identificados
Para la aplicación de gestión de pedidos, identificamos los siguientes microservicios:
- Servicio de Usuarios: Gestiona la información de los usuarios.
- Servicio de Productos: Gestiona el catálogo de productos.
- Servicio de Pedidos: Gestiona la creación y seguimiento de pedidos.
- Servicio de Notificaciones: Envía notificaciones a los usuarios sobre el estado de sus pedidos.
Diagrama de Arquitectura
+------------------+ +------------------+ +------------------+ | Servicio de | | Servicio de | | Servicio de | | Usuarios |<----->| Pedidos |<----->| Productos | +------------------+ +------------------+ +------------------+ | | | v v v +------------------+ +------------------+ +------------------+ | Servicio de | | Base de Datos | | Base de Datos | | Notificaciones | | de Pedidos | | de Productos | +------------------+ +------------------+ +------------------+ | v +------------------+ | Base de Datos | | de Usuarios | +------------------+
- Implementación de Microservicios
Servicio de Usuarios
Estructura del Proyecto
user-service/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── userservice/ │ │ │ ├── UserServiceApplication.java │ │ │ ├── controller/ │ │ │ │ └── UserController.java │ │ │ ├── model/ │ │ │ │ └── User.java │ │ │ └── repository/ │ │ │ └── UserRepository.java │ └── resources/ │ └── application.yml └── pom.xml
Código de Ejemplo
UserServiceApplication.java
package com.example.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } }
UserController.java
package com.example.userservice.controller; import com.example.userservice.model.User; import com.example.userservice.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/users") public class UserController { @Autowired private UserRepository userRepository; @GetMapping public List<User> getAllUsers() { return userRepository.findAll(); } @PostMapping public User createUser(@RequestBody User user) { return userRepository.save(user); } }
User.java
package com.example.userservice.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; // Getters and Setters }
UserRepository.java
package com.example.userservice.repository; import com.example.userservice.model.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { }
application.yml
spring: datasource: url: jdbc:mysql://localhost:3306/userdb username: root password: password jpa: hibernate: ddl-auto: update show-sql: true
Servicio de Productos
Estructura del Proyecto
product-service/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── productservice/ │ │ │ ├── ProductServiceApplication.java │ │ │ ├── controller/ │ │ │ │ └── ProductController.java │ │ │ ├── model/ │ │ │ │ └── Product.java │ │ │ └── repository/ │ │ │ └── ProductRepository.java │ └── resources/ │ └── application.yml └── pom.xml
Código de Ejemplo
ProductServiceApplication.java
package com.example.productservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ProductServiceApplication { public static void main(String[] args) { SpringApplication.run(ProductServiceApplication.class, args); } }
ProductController.java
package com.example.productservice.controller; import com.example.productservice.model.Product; import com.example.productservice.repository.ProductRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/products") public class ProductController { @Autowired private ProductRepository productRepository; @GetMapping public List<Product> getAllProducts() { return productRepository.findAll(); } @PostMapping public Product createProduct(@RequestBody Product product) { return productRepository.save(product); } }
Product.java
package com.example.productservice.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private Double price; // Getters and Setters }
ProductRepository.java
package com.example.productservice.repository; import com.example.productservice.model.Product; import org.springframework.data.jpa.repository.JpaRepository; public interface ProductRepository extends JpaRepository<Product, Long> { }
application.yml
spring: datasource: url: jdbc:mysql://localhost:3306/productdb username: root password: password jpa: hibernate: ddl-auto: update show-sql: true
Servicio de Pedidos
Estructura del Proyecto
order-service/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── orderservice/ │ │ │ ├── OrderServiceApplication.java │ │ │ ├── controller/ │ │ │ │ └── OrderController.java │ │ │ ├── model/ │ │ │ │ └── Order.java │ │ │ └── repository/ │ │ │ └── OrderRepository.java │ └── resources/ │ └── application.yml └── pom.xml
Código de Ejemplo
OrderServiceApplication.java
package com.example.orderservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } }
OrderController.java
package com.example.orderservice.controller; import com.example.orderservice.model.Order; import com.example.orderservice.repository.OrderRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/orders") public class OrderController { @Autowired private OrderRepository orderRepository; @GetMapping public List<Order> getAllOrders() { return orderRepository.findAll(); } @PostMapping public Order createOrder(@RequestBody Order order) { return orderRepository.save(order); } }
Order.java
package com.example.orderservice.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long userId; private Long productId; private Integer quantity; // Getters and Setters }
OrderRepository.java
package com.example.orderservice.repository; import com.example.orderservice.model.Order; import org.springframework.data.jpa.repository.JpaRepository; public interface OrderRepository extends JpaRepository<Order, Long> { }
application.yml
spring: datasource: url: jdbc:mysql://localhost:3306/orderdb username: root password: password jpa: hibernate: ddl-auto: update show-sql: true
Servicio de Notificaciones
Estructura del Proyecto
notification-service/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── notificationservice/ │ │ │ ├── NotificationServiceApplication.java │ │ │ ├── controller/ │ │ │ │ └── NotificationController.java │ │ │ ├── model/ │ │ │ │ └── Notification.java │ │ │ └── repository/ │ │ │ └── NotificationRepository.java │ └── resources/ │ └── application.yml └── pom.xml
Código de Ejemplo
NotificationServiceApplication.java
package com.example.notificationservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class NotificationServiceApplication { public static void main(String[] args) { SpringApplication.run(NotificationServiceApplication.class, args); } }
NotificationController.java
package com.example.notificationservice.controller; import com.example.notificationservice.model.Notification; import com.example.notificationservice.repository.NotificationRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/notifications") public class NotificationController { @Autowired private NotificationRepository notificationRepository; @GetMapping public List<Notification> getAllNotifications() { return notificationRepository.findAll(); } @PostMapping public Notification createNotification(@RequestBody Notification notification) { return notificationRepository.save(notification); } }
Notification.java
package com.example.notificationservice.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Notification { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long userId; private String message; // Getters and Setters }
NotificationRepository.java
package com.example.notificationservice.repository; import com.example.notificationservice.model.Notification; import org.springframework.data.jpa.repository.JpaRepository; public interface NotificationRepository extends JpaRepository<Notification, Long> { }
application.yml
spring: datasource: url: jdbc:mysql://localhost:3306/notificationdb username: root password: password jpa: hibernate: ddl-auto: update show-sql: true
- Configuración de la Comunicación entre Microservicios
Para la comunicación entre microservicios, utilizaremos APIs RESTful. Cada microservicio expone endpoints que pueden ser consumidos por otros microservicios.
Ejemplo de Comunicación
El Servicio de Pedidos necesita obtener información del Servicio de Usuarios y del Servicio de Productos para crear un pedido.
OrderController.java (modificado)
package com.example.orderservice.controller; import com.example.orderservice.model.Order; import com.example.orderservice.repository.OrderRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import java.util.List; @RestController @RequestMapping("/orders") public class OrderController { @Autowired private OrderRepository orderRepository; @Autowired private RestTemplate restTemplate; @GetMapping public List<Order> getAllOrders() { return orderRepository.findAll(); } @PostMapping public Order createOrder(@RequestBody Order order) { // Obtener información del usuario String userUrl = "http://localhost:8081/users/" + order.getUserId(); User user = restTemplate.getForObject(userUrl, User.class); // Obtener información del producto String productUrl = "http://localhost:8082/products/" + order.getProductId(); Product product = restTemplate.getForObject(productUrl, Product.class); // Lógica adicional para crear el pedido return orderRepository.save(order); } }
User.java (cliente)
package com.example.orderservice.controller; public class User { private Long id; private String name; private String email; // Getters and Setters }
Product.java (cliente)
package com.example.orderservice.controller; public class Product { private Long id; private String name; private Double price; // Getters and Setters }
- Despliegue con Docker
Dockerfile para el Servicio de Usuarios
Dockerfile
FROM openjdk:11-jre-slim COPY target/user-service-0.0.1-SNAPSHOT.jar user-service.jar ENTRYPOINT ["java", "-jar", "user-service.jar"]
Construcción y Ejecución del Contenedor
# Construir la imagen docker build -t user-service . # Ejecutar el contenedor docker run -d -p 8081:8080 --name user-service user-service
Repite el proceso para los otros microservicios cambiando los puertos y nombres de contenedores según sea necesario.
- Orquestación con Kubernetes
Despliegue de Kubernetes
user-service-deployment.yml
apiVersion: apps/v1 kind: Deployment metadata: name: user-service spec: replicas: 2 selector: matchLabels: app: user-service template: metadata: labels: app: user-service spec: containers: - name: user-service image: user-service:latest ports: - containerPort: 8080
user-service-service.yml
apiVersion: v1 kind: Service metadata: name: user-service spec: selector: app: user-service ports: - protocol: TCP port: 80 targetPort: 8080
Aplica los archivos de configuración:
Repite el proceso para los otros microservicios.
- Monitoreo y Logging
Configuración de Prometheus y Grafana
- Despliega Prometheus y Grafana en tu clúster de Kubernetes.
- Configura los microservicios para exportar métricas a Prometheus.
application.yml (modificado)
Configuración de ELK Stack (Elasticsearch, Logstash, Kibana)
- Despliega ELK Stack en tu clúster de Kubernetes.
- Configura los microservicios para enviar logs a Logstash.
logback-spring.xml
<configuration> <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender"> <destination>logstash:5000</destination> <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> </appender> <root level="INFO"> <appender-ref ref="LOGSTASH"/> </root> </configuration>
- Seguridad en Microservicios
Autenticación y Autorización
Implementa OAuth2 para la autenticación y autorización de los microservicios.
application.yml (modificado)
Seguridad en la Comunicación
Configura TLS para asegurar la comunicación entre los microservicios.
application.yml (modificado)
Conclusión
En esta sección, hemos implementado una aplicación completa basada en microservicios, cubriendo desde el diseño y desarrollo hasta el despliegue y monitoreo. Este ejercicio práctico te proporciona una comprensión integral de cómo construir y gestionar aplicaciones de microservicios en un entorno real. Asegúrate de revisar y experimentar con cada componente para consolidar tu conocimiento y habilidades en microservicios.
Curso de Microservicios
Módulo 1: Introducción a los Microservicios
- Conceptos Básicos de Microservicios
- Ventajas y Desventajas de los Microservicios
- Comparación con Arquitectura Monolítica
Módulo 2: Diseño de Microservicios
- Principios de Diseño de Microservicios
- Descomposición de Aplicaciones Monolíticas
- Definición de Bounded Contexts
Módulo 3: Comunicación entre Microservicios
Módulo 4: Implementación de Microservicios
- Elección de Tecnologías y Herramientas
- Desarrollo de un Microservicio Simple
- Gestión de Configuración