Hola a todos!
En este artículo, demostraremos los componentes básicos para crear microservicios RESTful utilizando el registro de servicios Consul, Spring Boot para todos los andamios, inyección de dependencias, Maven para ensamblaje, así como Spring REST y la API Java RESTful Jersey / JaxRS.
Las principales ventajas de los microservicios:
- Los microservicios ayudan a aflojar su código
- Los microservicios permiten que diferentes equipos trabajen en componentes pequeños utilizando tecnologías independientes, proporcionando una implementación más segura y frecuente. Spring Boot admite diversas implementaciones para crear una API REST
- El descubrimiento y la llamada de servicios no dependen de la plataforma de servicios.
- Swagger crea una sólida documentación de API e interfaz de llamadas
Si aún no está utilizando microservicios, no ha logrado ingresar a la fase de seguidores tempranos en la curva de percepción de la tecnología, y probablemente sea el momento adecuado para comenzar.

En las últimas dos décadas, la empresa se ha vuelto muy flexible en nuestro proceso SDLC, pero nuestras aplicaciones, por regla general, siguen siendo monolíticas, con enormes frascos que admiten todas las diversas API y versiones en el mercado. Pero en la actualidad, existe el deseo de más procesos Lean, DevOps, y la funcionalidad se está volviendo "sin servidor". Refactorizar a microservicios puede reducir el enredo de código y recursos, hacer que los ensamblajes sean más pequeños, las versiones son más seguras y las API son más estables.
En este artículo, crearemos una aplicación simple de gestión de cartera de bolsa a la que los clientes pueden llamar para evaluar su cartera de acciones (valores y valores de acciones). El microservicio de cartera recuperará la cartera del cliente, la enviará al microservicio de fijación de precios para aplicar los precios más recientes y luego devolverá la cartera totalmente valorada y subtotalizada, demostrando todo esto mediante una llamada de descanso.

Antes de comenzar a trabajar en la creación de nuestros microservicios, preparemos nuestro entorno configurando Consul.
Descargar Consul
Utilizaremos Hashicorp Consul para descubrir servicios, así que vaya a
www.consul.io/downloads.html y descargue Consul para Windows, Linux, Mac, etc. Esto le proporcionará un archivo ejecutable que debe agregar a su ruta.
Lanzar cónsul
En el símbolo del sistema, iniciando Consul en modo dev:
consul agent -dev
Para verificar que se está ejecutando, vaya a su navegador y acceda a la interfaz de cónsul
http: // localhost: 8500 . Si todo va bien, el cónsul debe informar que está vivo y bien. Al hacer clic en el servicio de cónsul (a la izquierda), recibirá información adicional (a la derecha).

Si en este momento hay algún problema, asegúrese de agregar Consul a la ruta de ejecución, y los puertos 8500 y 8600 están disponibles.
Crear una aplicación SpringBoot
Utilizaremos
Spring Initializr , que está integrado en la mayoría de los IDE, para construir nuestras aplicaciones SpringBoot. Las capturas de pantalla a continuación usan IntelliJ IDEA.
Seleccione "Archivo / Nuevo proyecto" para abrir una nueva plantilla de proyecto y luego "Spring Initializr".

En general, puede configurar el andamiaje sin un IDE completando un formulario en línea a través de la página web SpringBoot Initializr
start.spring.io , que creará un archivo zip para su proyecto vacío, listo para descargar.
Haga clic en "Siguiente" y complete los metadatos del proyecto. Use la siguiente configuración:

Haga clic en "Siguiente" para seleccionar las dependencias y escriba "Jersey" y "Consul Discovery" en la búsqueda de dependencias. Agregue estas dependencias:

Haga clic en "Siguiente" para indicar el nombre del proyecto y su ubicación. Mantenga el nombre predeterminado "cartera" y especifique la ubicación preferida del proyecto, luego haga clic en "finalizar" para crear y abrir el proyecto:

Podemos usar el application.properties generado, pero SpringBoot también reconoce el formato YAML, que es un poco más fácil de visualizar, así que cambiemos el nombre a application.yml.
Llamamos al microservicio "portfolio-service". Podemos especificar un puerto o usar el puerto 0 para que la aplicación use un puerto disponible. En nuestro caso, utilizaremos 57116. Si aloja este servicio como un contenedor Docker, puede asignarlo a cualquier puerto que seleccione. Asigne un nombre a la aplicación y especifique nuestro puerto agregando lo siguiente a nuestra application.yml:
spring: application: name: portfolio-service server: port: 57116
Para que nuestro servicio esté disponible, agregue anotaciones a nuestra clase de aplicación SpringBoot. Abra la aplicación PortfolioApplication y agregue @EnableDiscoveryClient encima de la declaración de clase.
Confirmar las importaciones. La clase debería verse así:
package com.restms.demo.portfolio; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; . . . @SpringBootApplication @EnableDiscoveryClient public class PortfolioApplication { public static void main(String[] args) { SpringApplication.run(PortfolioApplication.class, args); } }
(Para demostrar cómo los microservicios pueden consistir en plataformas independientes, usaremos Jersey para este servicio y Spring REST para el próximo).
Para configurar el servicio web RESTful en Jersey, necesitamos especificar la clase de configuración ResourceConfig. Agregue la clase JerseyConfig (para demostración, la guardaremos en el mismo paquete que nuestra clase de aplicación). Debería verse así, más el paquete correcto e importar:
@Configuration @ApplicationPath("portfolios") public class JerseyConfig extends ResourceConfig { public JerseyConfig() { register(PortfolioImpl.class); } }
Tenga en cuenta que hereda de ResourceConfig para designarlo como una clase de configuración Jersey. El atributo @ApplicationPath ("portafolios") define el contexto de la llamada, lo que significa que las llamadas deben comenzar con el elemento de ruta "portfolioios". (Si lo omite, el contexto predeterminado es "/").
La clase PortfolioImpl atenderá dos solicitudes: portafolios / customer / {customer-id} devuelve todos los portafolios y portafolios / customer / {customer-id} / portfolio / {portfolio-id} devuelve un portfolio. Una cartera consta de un conjunto de tickers y el número de acciones que posee ese ticker. (Para demostración, hay tres clientes con identificadores 0, 1 y 2, cada uno de los cuales tiene tres carteras con identificadores 0, 1 y 2).
Su IDE le pedirá que cree PortfolioImpl; hazlo ahora. Para demostrarlo, agréguelo al mismo paquete. Ingrese el código a continuación y confirme todas las importaciones:
@Component @Path("/") public class PortfolioImpl implements InitializingBean { private Object[][][][] clientPortfolios; @GET @Path("customer/{customer-id}") @Produces(MediaType.APPLICATION_JSON) // a portfolio consists of an array of arrays, each containing an array of // stock ticker and associated shares public Object[][][] getPortfolios(@PathParam("customer-id") int customerId) { return clientPortfolios[customerId]; } @GET @Path("customer/{customer-id}/portfolio/{portfolio-id}") @Produces(MediaType.APPLICATION_JSON) public Object[][] getPortfolio(@PathParam("customer-id") int customerId, @PathParam("portfolio-id") int portfolioId) { return getPortfolios(customerId)[portfolioId]; } @Override public void afterPropertiesSet() throws Exception { Object[][][][] clientPortfolios = { { // 3 customers, 3 portfolios each {new Object[]{"JPM", 10201}, new Object[]{"GE", 20400}, new Object[]{"UTX", 38892}}, {new Object[]{"KO", 12449}, new Object[]{"JPM", 23454}, new Object[]{"MRK", 45344}}, {new Object[]{"WMT", 39583}, new Object[]{"DIS", 95867}, new Object[]{"TRV", 384756}}, }, { {new Object[]{"GE", 38475}, new Object[]{"MCD", 12395}, new Object[]{"IBM", 91234}}, {new Object[]{"VZ", 22342}, new Object[]{"AXP", 385432}, new Object[]{"UTX", 23432}}, {new Object[]{"IBM", 18343}, new Object[]{"DIS", 45673}, new Object[]{"AAPL", 23456}}, }, { {new Object[]{"AXP", 34543}, new Object[]{"TRV", 55322}, new Object[]{"NKE", 45642}}, {new Object[]{"CVX", 44332}, new Object[]{"JPM", 12453}, new Object[]{"JNJ", 45433}}, {new Object[]{"MRK", 32346}, new Object[]{"UTX", 46532}, new Object[]{"TRV", 45663}}, } }; this.clientPortfolios = clientPortfolios; } }
La anotación
Componente designa esto como la clase del componente Spring y lo proporciona como un punto final.
Las anotaciones de
ruta sobre la declaración de clase declaran que se accede a la clase a través del elemento de ruta "/", y dos llamadas api compatibles están disponibles a través de portafolios / customer / {customer-id} y portafolios / customer / {customer-id} / portfolio / {portfolio- id}, como vemos en las anotaciones del método. Tenga en cuenta que la ruta ("/") es la predeterminada, pero la dejamos como referencia. Los métodos se designan como HTTP GET a través de @GETannotation. Nuestro método está diseñado para devolver una matriz y anotado para devolver Json, por lo que devuelve una matriz Json. Observe cómo se usan las anotaciones de parámetros de
ruta en la firma del método para extraer los parámetros mostrados de las consultas mostradas.
(Para nuestra demostración, devolvemos valores codificados. Por supuesto, en la práctica, la implementación consultará la base de datos u otro servicio o fuente de datos en lugar del código rígido).
Ahora cree un proyecto y ejecútelo. Si usa IntelliJ, creará un ejecutable predeterminado, así que simplemente haga clic en la flecha verde "ejecutar". También puedes usar
mvn spring-boot:run
O puede realizar la instalación de Maven y ejecutar la aplicación usando java -jar, apuntando al jar generado en el directorio de destino:
java -jar target\portfolio-0.0.1-SNAPSHOT.jar
Ahora deberíamos ver este servicio en Consul, así que volvamos a nuestro navegador, descargue
http: // localhost: 8500 / ui / # / dc1 / services (o actualícelo si ya está allí).

Hmm, vemos nuestro servicio allí, pero se muestra como fallido. Esto se debe a que Cónsul espera una señal de latido “saludable” de nuestro servicio.
Para generar señales de latido, podemos agregar una dependencia del servicio
Spring Actuator al pom de nuestra aplicación.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Mientras estamos en pom, tenga en cuenta que hay un conflicto de versiones con Jersey entre el titular Cónsul y el titular de Jersey. Para suavizar esto, designe al abridor de Jersey como la primera adicción.
Su pom ahora debe contener las siguientes dependencias:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
Al reiniciar Cónsul, el servicio de Portafolio muestra uno feliz:

Ahora en el servicio de cartera hay dos nodos de transmisión: uno de ellos es nuestra implementación del servicio de cartera y el otro es el latido del corazón.
Verifiquemos el puerto que ha sido asignado. Puede ver eso en el resultado de la aplicación:
INFO 19792 --- [ main] sbcetTomcatEmbeddedServletContainer : Tomcat started on port(s): 57116 (http)
También puede ver el puerto directamente en la interfaz de usuario de Consul. Haga clic en “servicio al cliente”, luego seleccione el enlace “Comprobar enlace de servicio al cliente”, que muestra el puerto de servicio, en este caso 57116.

Solicite
http: // localhost: 57116 / portfolios / customer / 1 / portfolio / 2 y verá la matriz json [["IBM", 18343], ["DIS", 45673], ["AAPL", 23456]]
¡Nuestro primer microservicio está abierto para los negocios!
Servicio de precios
A continuación, crearemos nuestro servicio de precios, esta vez usando Spring RestController en lugar de Jersey.
El servicio de fijación de precios aceptará el identificador de cliente y el identificador de cartera como parámetros y utilizará RestTemplate para solicitar servicios de cartera, recibir tickers y acciones, y devolver los precios actuales. (No necesito decirte que estos valores son noticias falsas, ¡así que no los uses para tomar decisiones comerciales!)
Cree un nuevo proyecto utilizando la siguiente información:

Esta vez, seleccione las dependencias Web, Disco Discovery y Actuator:

Deje el nombre predeterminado del proyecto "fijación de precios" y cree un proyecto en el directorio que elija.
Esta vez usaremos application.properties en lugar de application.yml.
Establezca el nombre y el puerto en application.properties como:
spring.application.name=pricing server.port=57216
Anote la aplicación de precios con @EnableDiscoveryClient. La clase debería verse así, más el paquete y la importación.
@SpringBootApplication @EnableDiscoveryClient public class PricingApplication { public static void main(String[] args) { SpringApplication.run(PricingApplication.class, args); } }
Luego crearemos la clase PricingEndpoint. Aquí daré un ejemplo más detallado, ya que muestra varias funciones importantes, incluido el descubrimiento de servicios (búsqueda de servicios de cartera) y el uso de RestTemplate para la consulta:
@RestController @RequestMapping("/pricing") public class PricingEndpoint implements InitializingBean { @Autowired DiscoveryClient client; Map<String, Double> pricingMap = new HashMap<>(); RestTemplate restTemplate = new RestTemplate(); @GetMapping("/customer/{customer-id}/portfolio/{portfolio-id}") public List<String> getPricedPortfolio( @PathVariable("customer-id") Integer customerId, @PathVariable("portfolio-id") Integer portfolioId) { List<ServiceInstance> instances = client.getInstances("portfolio-service"); ServiceInstance instance = instances.stream() .findFirst() .orElseThrow(() -> new RuntimeException("not found")); String url = String.format("%s/portfolios/customer/%d/portfolio/%d", instance.getUri(), customerId, portfolioId); // query for the portfolios, returned as an array of List // of size 2, containing a ticker and a position (
Para encontrar un servicio de cartera, necesitamos tener acceso a DiscoveryClient. Es fácil de obtener con la anotación @Autowired de Spring.
@Autowired DiscoveryClient client;
Esta instancia de DiscoveryClient se usa para buscar el servicio en la llamada:
List<ServiceInstance> instances = client.getInstances("portfolio-service"); ServiceInstance instance = instances.stream().findFirst().orElseThrow(() -> new RuntimeException("not found"));
Después de encontrar el servicio, podemos usarlo para cumplir con nuestra solicitud, que redactamos de acuerdo con la llamada API creada en nuestro servicio de cartera.
String url = String.format("%s/portfolios/customer/%d/portfolio/%d", instance.getUri(), customerId, portfolioId);
Finalmente, usamos RestTemplate para ejecutar nuestra solicitud GET.
Object[] portfolio = restTemplate.getForObject(url, Object[].class);
Tenga en cuenta que para los controladores Rest (así como para Spring MVC Request Controller), las variables de ruta se recuperan utilizando la anotación de Variable de
ruta , a diferencia de Jersey, que, como vimos, usa
Path Param.
Esto concluye nuestros precios con Spring RestController.
La documentación
Resolvimos todos estos problemas para crear nuestros microservicios, pero no traerán suficientes beneficios si no le damos al mundo conocimiento sobre cómo usarlos.
Para hacer esto, utilizamos la herramienta
Swagger conveniente y fácil de usar, que no solo documenta nuestras llamadas API, sino que también proporciona un cliente web conveniente para llamarlas.
Primero, especifiquemos Swagger en nuestro pom:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
Luego tenemos que decirle a Swagger cuál de nuestras clases queremos documentar. Presentemos la nueva clase SwaggerConfig que contiene la especificación Swagger.
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.regex("/pricing.*")) .build(); } }
Veamos qué hace esta clase. Primero, designamos esto como una configuración Swagger con la anotación @ EnableSwagger2.
Luego creamos un componente Docket que le dice a Swagger qué API deben mostrarse. En el ejemplo anterior, le dijimos a Swagger que demostrara cualquier camino que comience con "/ price". Una alternativa sería especificar clases para documentación, y no para rutas:
.apis(RequestHandlerSelectors.basePackage("com.restms.demo")) .paths(PathSelectors.any())
Reinicie el microservicio de precios y llame al
http: // localhost: 57216 / swagger-ui.html desde el navegador

Haga clic en Lista de operaciones para ver las operaciones de servicio en detalle.
Haga clic en Expandir operaciones para crear una solicitud basada en el formulario. Establezca algunos parámetros, haga clic en "¡Pruébelo!" y espera la respuesta:

Puede agregar muchos más colores agregando anotaciones Swagger a sus métodos.
Por ejemplo, decore el método PricingImpl.getPricedPortfolio existente utilizando la anotación @ApiOperation, como se muestra a continuación:
@ApiOperation(value = "Retrieves a fully priced portfolio", notes = "Retrieves fully priced portfolio given customer id and portfolio id") @GetMapping("/customer/{customer-id}/portfolio/{portfolio-id}") public List<String> getPricedPortfolio(@PathVariable("customer-id") Integer customerId, @PathVariable("portfolio-id") Integer portfolioId)
Vuelva a cargar y actualice swagger-ui para ver la nueva documentación actualizada:

Y eso no es todo lo que puede hacer con Swagger, así que consulte la documentación.
Yuri Dvorzhetsky , profesor de nuestro curso
"Desarrollador en Spring Framework", le contará más sobre el trabajo de Spring Boot:
Artículo original