BLOG

Programación-Tecnología

Los microservicios suponen un nuevo estilo de arquitectura software para el diseño de aplicaciones. En este post explicaré como  crear y desplegar microservicios desde cero utilizando Spring Boot, uno de los frameworks más populares para su construcción.

Pero, ¿qué son los microservicios?

Según Martin Fowler y James Lewis explican en su artículo Microservices, los microservicios se definen como un estilo arquitectural, es decir, una forma de desarrollar una aplicación, basada en un conjunto de pequeños servicios, cada uno de ellos ejecutándose de forma autónoma y comunicándose entre si mediante mecanismos livianos, generalmente a través de peticiones REST sobre HTTP por medio de sus APIs.

La tendencia es que las aplicaciones sean diseñadas con un enfoque orientado a microservicios, construyendo múltiples servicios que colaboran entre si, en lugar del enfoque monolítico, donde se construye y despliega una única aplicación que contenga todas las funcionalidades.

Algunas de las características más destacadas de los microservicios son:

  • Pueden ser auto-contenidos, de tal forma que incluyen todo lo necesario para prestar su servicio
  • Servicios pequeños, lo que facilita el mantenimiento. Ej: Personas, Productos, Posición Global, etc
  • Principio de responsabilidad única: cada microservicio hará una única cosa, pero la hará bien
  • Políglotas: una arquitectura basada en microservicios facilita la integración entre diferentes tecnologías (lenguajes de programación, BBDD…etc)
  • Despliegues unitarios: los microservicios pueden ser desplegados por separado, lo que garantiza que cada despliegue de un microservicio no implica un despliegue de toda la plataforma. Tienen la posibilidad de incorporar un servidor web embebido como Tomcat o Jetty  
  • Escalado eficiente: una arquitectura basada en microservicios permite un escalado elástico horizontal, pudiendo crear tantas instancias de un microservicio como sea necesario.

Sobre el ejercicio práctico

Voy a intentar explicar, de la manera más simple posible, como construir una sencilla aplicación compuesta por tres microservicios:

 

Exercise with microservices

El objetivo es implementar un típico Hola Mundo con microservicios. Para ello, greeting-client-service llamará al método greeting del microservicio greeting-service usando RESTful API, pasándole como parámetro de entrada un nombre (Ej: “Rob”) y recibiendo como contestación un saludo (Ej: “Hello, Rob!”).

Para realizar el ejercicio, utilizaremos Eureka (registration-service), como servidor de registro y descubrimiento de microservicios. Eureka está incorporado dentro de Spring Cloud.

Puede descargar el código fuente del ejemplo en este enlace:  https://github.com/rcrespop/helloWorldEureka.git

Configuración

Para la implementación de los microservicios, utilizaremos Spring Boot y Spring Cloud.  El ejemplo estará compuesto de tres proyectos (uno por cada microservicio). A continuación muestro la configuración de los ficheros POM.xml.

<parent>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-parent</artifactId>
	<version>Brixton.RELEASE</version>
</parent>

<groupId>net.robertocrespo</groupId>
<artifactId>microservice-XXXX</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>microservice-greeting</name>
<url>https://maven.apache.org</url>

<dependencies>
	<dependency>
		<!-- Setup Spring Boot -->
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>

	<dependency>
		<!-- Setup Spring MVC & REST, use Embedded Tomcat -->
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>

	<dependency>
		<!-- Spring Cloud starter -->
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-thymeleaf</artifactId>
	</dependency>
	<dependency>
		<!-- Eureka for service registration -->
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-eureka-server</artifactId>
	</dependency>
</dependencies>

Microservicio Registration

Como comenté antes, para implementar este servicio utilizaremos Eureka, un servidor de registro y descubrimiento de microservicios open source desarrollado por Netflix. Levantar una instancia de este servidor con Spring Cloud es muy sencillo. Aquí tienes el código completo:

@SpringBootApplication
@EnableEurekaServer
public class RegistrationServer {

	public static void main(String[] args) {
		SpringApplication.run(RegistrationServer.class, args);
	}
}

La anotación @SpringBootApplication indica que se trata de una aplicación Spring Boot y @EnableEurekaServer habilita el uso Eureka como servidor de registro y descubrimiento de microservicios.

Por defecto, cuando inicias una aplicación spring boot , se busca un fichero llamado application.properties o application.yml para acceder a su configuración, el cual deberá estar ubicado en la carpeta resources de nuestro proyecto. Su configuración es la siguiente:

# Configure this Discovery Server
eureka:
  instance:
    hostname: localhost
  client:  # Not a client, don't register with yourself
    registerWithEureka: false
    fetchRegistry: false

server:
  port: 1111   # HTTP (Tomcat) port

# Discovery Server Dashboard uses FreeMarker.  Don't want Thymeleaf templates
spring:
  thymeleaf:
    enabled: false     # Disable Thymeleaf

Observa que:

  • En la configuración del fichero yml estamos indicando que no se trata de un microservicio cliente y que por tanto no queremos que se registre en Eureka (registerWithEureka: false).
  • Aunque por defecto Eureka escucha en el puerto 8761, para este ejemplo hemos indicado que utilice el puerto 1111. Prueba a ejecutar el servicio, comprueba que el servidor arranca correctamente y accede a https://localhost:1111 para visualizar el dashboard de Eureka.
  • El dashboard de Eureka está implementado usando plantillas FreeMarket (los otros dos microservicios que crearemos a continuación usarán thymeleaf para construir las vistas HTML.

Eukera Dashboard

 

Microservicio Greeting

Este microservicio gestionará peticiones que reciba por HTTP para devolver un saludo (greeting) a los nombres de las personas que reciba como entrada. Al arrancar greeting-service lo primero que hará será registrarse en Eureka.

@EnableAutoConfiguration
@EnableDiscoveryClient
@SpringBootApplication
public class GreetingServer {

	public static void main(String[] args) {
		SpringApplication.run(GreetingServer.class, args);
	}
}

Respecto a las anotaciones utilizadas:

  • @SpringBootApplication define que es una aplicación Spring Boot
  • @EnableAutoConfiguration configura la aplicación sin necesidad de otros ficheros de configuración XML, etc.
  • @EnableDiscoveryClient  habilita el servicio de registro y descubrimiento. En este caso, este procesos se auto-registrará en Eureka utilizando el nombre de aplicación indicado en el fichero greeting-server.yml cuya configuración se muestra a continuación:
# Spring properties
spring:
  application:
     name: greeting-service  # Service registers under this name
  freemarker:
    enabled: false           # Ignore Eureka dashboard FreeMarker templates
  thymeleaf:
    cache: false             # Allow Thymeleaf templates to be reloaded at runtime
    prefix: classpath:/templates/    # Trailing / mandatory
                             # Template location for this application only

# Discovery Server Access
eureka:
  client:
    serviceUrl:
      defaultZone: https://localhost:1111/eureka/

# HTTP Server
server:
  port: 2222   # HTTP (Tomcat) port

En la configuración del fichero greeting-server.yml observamos que:

  • El nombre de la aplicación es greeting-service, que será utilizado para registrarse en Eureka
  • Se configura el puerto de escucha del servidor web (2222). Esto es necesario ya que vamos a ejecutar varios procesos que usan Tomcat y todos ellos no pueden estar escuchando al mismo tiempo del puerto 8080.
  • Se establece la URL de acceso al servicio de Eureka

Este microservicio utilizará Spring REST para ofrecer un interface RESTful sobre HTTP y poder así acceder a sus operaciones.

@RestController
public class GreetingController {

	private static final String template = "Hello, %s!";

	@RequestMapping("/greeting/{name}")
    	public Greeting greeting(@PathVariable("name") String name) {
        	return new Greeting(String.format(template, name));
    	}
}

El método greeting devolverá un objeto de tipo Greeting, cuya clase se muestra a continuación:

public class Greeting implements Serializable {

    private static final long serialVersionUID = 1L;
    private final String content;

    public Greeting(String content) {
        this.content = content;
    }
    public String getContent() {
        return content;
    }
}

Por último, ejecuta el microservicio greeting-service (método main de la clase GreetingServer) y comprueba que al refrescar el dahboard de Eureka, aparece el servicio registrado. Ten en cuenta que el registro puede durar entre 10-20 segundos en realizarse.

Register Greeting-Service

Comprueba que el microservicio responde correctamente al realizar una petición RESTFul vía HTTP como la siguiente: https://localhost:2222//greeting/Rob

REST Greeting-Service

 

Microservicio ClientGreeting

El objetivo de este microservicio será, a través de  Eureka (registration-service), localizar la URL de acceso al microservicio greeting-service, hacer una petición REST sobre HTTP a uno de sus métodos expuestos en su API, obtener un resultado y presentarlo en pantalla usando thymeleaf.

Para consumir un servicio RESTful, Spring proporcionar la clase RestTemplate que nos permite enviar peticiones HTTP a un servidor RESTful y recuperar los datos en formatos como JSON y XML. En nuestro ejemplo usaremos JSON como formato de intercambio de datos, para lo cual vamos a apoyarnos en Jackson, una librería java para el procesamiento y parseo de datos JSON.

la implementación de este microservicio estará compuesta de tres pasos

Paso 1: Implementación del Service

Implementaremos el acceso al microservicio greeting-service en la siguiente clase:

@Service
public class ClientGreetingService {

	@Autowired
	protected RestTemplate restTemplate;
	protected String serviceUrl;

	public ClientGreetingService(String serviceUrl) {
		this.serviceUrl = serviceUrl.startsWith("http") ? serviceUrl: "https://" + serviceUrl;
	}

	//invoke to greeting-service and return a Greeting object
	public Greeting greeting (String name) {
		Greeting greeting =  restTemplate.getForObject(serviceUrl + "/greeting/{name}",Greeting.class, name);

		return greeting;
	}
}

RestTemplate ha sido configurado por Spring Cloud para usar un HttpRequestClient personalizado que utiliza Netflix Ribbon para realizar la búsqueda del microservicio. Ribbon además es un balanceador de carga, por lo que si tienes varias instancias disponibles de un microservicio, selecciona una para ti.

La petición REST realizada en la clase ClientGreetingService devuelve un mensaje JSON que pasearemos, usando Jackson, a un objeto de la clase Greeting. A continuación muestro la definición de esta clase:

import com.fasterxml.jackson.annotation.JsonRootName;

@JsonRootName("Greeting")
public class Greeting {
    protected String content;

    protected Greeting() {
	this.content = "Hello!";
}

    public Greeting(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}
Paso 2: Implementación de Controller

A continuación vamos a crear un controlador que va a agrupar un conjunto de acciones a realizar sobre este microservicio. Para ello usaremos las siguientes anotaciones:

  • @Controller, que registrará el controlador para Spring MVC
  • @RequestMapping, anotación que se encarga de relacionar un método con una petición http
@Controller
public class ClientGreetingController {

	protected ClientGreetingService helloWorldService;

	//constructor
	public ClientGreetingController(ClientGreetingService helloWorldService) {
		this.helloWorldService = helloWorldService;
	}

	@RequestMapping("/greeting")
	public String goHome() {
		return "index";
	}

	@RequestMapping("/greeting/{name}")
	public String greeting(Model model, @PathVariable("name") String name) {

		Greeting greeting = helloWorldService.greeting(name);

		model.addAttribute("greeting", greeting.getContent());

		return "greeting";
	}
}

@Controller
public class ClientGreetingHomeController {

	@RequestMapping("/")
	public String home() {
		return "index";
	}
}

ClientGreetingController es un típico controlador Spring MVC que devolverá un HTML. Para construir la vista y generar dinámicamente el HTML, la aplicación usará Thymeleaf.

Paso 3: Implementación del Microservicio

Por último creamos el microservicio greeting-client-service, que proporcionará el valor de la variable serviceURL a ClientGreetingController, el cual a su vez se lo pasará a ClientGreetingService

@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan(useDefaultFilters = false) // Disable component scanner
public class ClientGreetingServer {

	public static final String SERVICE_URL = "https://GREETING-SERVICE";

	public static void main(String[] args) {
		SpringApplication.run(ClientGreetingServer.class, args);
	}

        //A customized RestTemplate that has the ribbon load balancer build in
        @LoadBalanced
	@Bean
	RestTemplate restTemplate() {
		return new RestTemplate();
	}

	// The service encapsulates the interaction with the micro-service.
	@Bean
	public ClientGreetingService helloWorldService() {
		return new ClientGreetingService(SERVICE_URL);
	}

	//Create the controller, passing it the ClientGreetingService to use.
	@Bean
	public ClientGreetingController helloWorldController() {
		return new ClientGreetingController(helloWorldService());
	}

	@Bean
	public ClientGreetingHomeController homeController() {
		return new ClientGreetingHomeController();
	}
}

Ten en cuenta que Greetingclientservice, además de ser un microservicio y auto-registrarse en el registro de servicios (@EnableDiscoveryClient), utilizará Eureka para localizar el microservicio greeting-service.

El contenido del fichero de configuración de este microservicio es el siguiente:

# Spring properties
spring:
  application:
    name: greeting-client-service  # Identify this application
  freemarker:
    enabled: false     # Ignore Eureka dashboard FreeMarker templates
  thymeleaf:
    cache: false       # Allow Thymeleaf templates to be reloaded at runtime
    prefix: classpath:/templates/    # Trailing / mandatory
                       # Template location for this application onlyy

# Map the error path to error template (for Thymeleaf)
error:
  path=/error

# Discovery Server Access
eureka:
  client:
    serviceUrl:
      defaultZone: https://localhost:1111/eureka/

# HTTP Server
server:
  port: 3333   # HTTP (Tomcat) port

Ya solo queda ejecutar este microservicio y probar que todo funciona correctamente. Comprueba que al levantar el microservicio, aparece registrado en la consola de Eureka

Registro Greeting-Client-Service

El microservicio client-greeting-service está levantado y escuchando peticiones en el puerto 3333. Accede a https://localhost:3333 y comprueba que visualizas la siguiente pantalla:

Greeting-Client-Service

 

Si pinchas en el link de Fetch by Name, el microservicio client-greeting-service llamará a Eureka para localizar el endpoint del microservicio greeting-service y a continuación realizará una petición REST para llamar al método greeting de este servicio pasándole como parámetro un nombre (“Rob”). A continuación muestro el resultado:

Greeting-Client-Service Test

Prueba a cambiar el nombre que se pasa como parámetro y comprueba que el resultado devuelto es distinto. Te animo a crear nuevas operaciones en este microservicio greeting-service y probar a invocarlas así como a crear nuevos microservicios un poco más complejos para la gestión de conceptos como usuarios, productos, etc.

Referencias

 

Comparte este artículo si te gustó:

19 Responses

  1. Hola Roberto, muy buen ejemplo.
    Tengo una duda, Como esto son microservicios, escalarán.
    Entonces ¿cómo se gestiona Eureka para asignar diferentes puertos de las nuevas instancias sin colisiones?
    O si pones un load balancer por delante para no tener que preocuparte de cuántos microservicios hay, ¿para qué quieres Eureka?
    A ver si puedes acclararme un poco este punto.
    Gracias!!

  2. Una consulta, no entiendo bien la arquitectura, en total, serian proyectos independientes?, uno para eureka, otro para el servicio y otro para el cliente?, por que al meter todo en uno solo me da error por el puerto.

  3. Estimado tu post está perfecto. Muchas gracias por compartir tus conocimientos.

    Y comenzaré a aplicar esta nueva arquitectura orientado a micro servicios en mis proyectos personales y del trabajo.

    Saludos desde perú.

  4. Estimado Buenas Tardes,

    Realmente me parecio un gran aporte tu post en lo referente a microservicio, agradecerte por el apoyo que brindas frente a esta nueva arquitectura que va posicionandose en el mercado a gran escala.

    Tengo una consulta en lo referente al microservicio que actua como cliente en el puerto 3333, he tratado de levantarlo con exito en erueka sin embargo al tratar de acceder a la interfaz me pide usuario y contraseña y no me deja ver la ultima pantalla web que muestras en el tutorial.

    Gracias!

  5. Saludos,
    Cuando trate de correr los microservicios que corren en el puerto 2222 y 3333 tuve errores para correrlos localmente, tuve que agregar la clase principal al pom de los respectivos proyectos en la seccion properties de la siguiente manera y levantaron:

    org.microservice.greeting.GreetingServer
    org.microservice.clientgreeting.ClientGreetingServer

    Mi pregunta es, ¿esta bien lo que hice o hay algo que hice mal? a posterior me salio este warning cuando accedo a https://localhost:1111/:

    EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

    Muchas gracias por el tutorial, me parece buenisimo… saludos!

    • Hola Anthony. Agradezco mucho tus comentarios y me alegra que el tutorial te esté resultado útil.
      Respecto a lo que preguntas, realmente no hiciste nada mal. La mejora que introdujiste es correcta y hace el código de tu aplicación un poco más consistente ya que indicas de manera inequívoca la clase principal de tu aplicación que contiene el método main, lo que te te facilita ejecutar sin problema tu aplicación si estás ubicado en el nodo raiz de tu proyecto . En el caso de no incluir esta info en el pom.xml de tu proyecto, simplemente tendrías que situarte sobre la clase que contiene el método main y lanzar la ejecución.

      Saludos

  6. ¡Muy buen artículo Roberto! Solo un pequeño inciso. ¿»Posición global» realmente se puede considerar como un micro servicio? Al depender de muchas aplicaciones, ¿no sería más bien un servicio compuesto por «n» micro servicios?

    • Hola Phil! lo primero gracias por tu comentario. Interesante cuestión. Aunque efectivamente se intenta que los microservicios sean los más autónomos e independientes, en soluciones reales es difícil que así sea siempre. Un servicio como el de posición global podríamos considerarlo de agregación, lo que quiere decir que se se apoyará en otros servicios para obtener un resultado final, aportando valor en el proceso. Es mi opinión, esto no resta para que sea considerado un microservicio, lo que pasa es que pertenecería a una capa de arquitectura (agregación) y sus funciones serían distintas a la de otros microservicos clasificados en una una capa core (tarjetas, cuentas, etc). En cualquier caso, si te apetece, encantado de que me comentes tu visión sobre este asunto.
      Un abrazo

  7. Estimado, antes que todo muchas gracias por este tutorial, aunque lo vengo a ver recien este año :p

    He estado hace un par de meses introducioendome en el desarrollo de Spring y me ha encantado este…. no se si decirlo framework porque va mucho mas alla, es casi un lenguaje nuevo.

    También he estado trabajando con lo que es Angular y mi consulta es la siguiente, ¿Podría obviarse el client-service si tengo una aplicacion Angular que este consumiendo el servicio RESTful?

    y por otro lado, ¿Por qué en «ClientGreetingController» se define el «ClientGreetingService» como «@Autowired» si en su constructor le esto pasando este servicio, ante todo muchas gracias y espero su respuesta.

    • Hola Matias

      Lo primero de todo agradecerte tu comentario y pedirte disculpas por la demora en la respuesta.Respecto a tu primera pregunta, por supuesto que es posible consumir desde una aplicación angularJS el API REST del microservicio greeting. En el ejemplo utilicé el clientgreeting para ilustrar como un microservicio puede registrarse y localizar otro microservicio por medio de un servicio de registro y descubrimiento como es Eureka.
      En cuanto a tu segunda pregunta, efectivamente no es necesario usar @Autowired para este caso. Se trata de un error en el código de ejemplo. Lo actualizo en el post para evitar confusiones (Gracias!)http://robertocrespo.net/wp-admin/edit-comments.php#comments-form

      Saludos

  8. Buenas tardes, no consigo comprender por completo el paradigma sin embargo para tener la oportunidad de emitir un juicio como siempre prefiero poner en practica las ideas y después hablar con conocimiento de causa.

    En este ejemplo tengo una duda, como es que al iniciar el servidor se asocia de forma automatica el servicio rest, es decir he conseguido que arranque mi servidor de registro y el GreetingService, aparece registrado el «servicio» sin embargo al tratar de acceder el servicio REST via su URL me encuentro con un error 404 y al tratar de ver en que momento se asocia el servicio rest al servidor GreetingService no logro visualizarlo.

    • Hola Hugo.

      Si te fijas, en el fichero de configuración de Gretting-Service (application.yml) verás que se indica el endpoint donde Eureka está levantado escuchando. Utilizando la anotación @EnableDiscoveryClient, habilitamos el registro del microservicio, el cual se registrará en Eureka al ejecutarse (utilizando la configuración contenida en application.yml). Si todo fue bien, deberías poder ver el servicio en el dashboard de Eureka y acceder al mismo por medio de la única operación REST que ofrece (ej: GET https://localhost:2222/greeting/Hugo)

      Saludos

Deja una respuesta

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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.