Microservices mit Spring-Boot

In diesem Artikel zeige ich, an einem Praxisbeispiel, wie mit Spring Boot sehr schnell ein Microservice gebaut und ausgeführt werden kann. Ich zeige, wie man einen REST-Service baut und ausführt. Im Anschluss wird der Microservice um ein UI erweitert, ich zeige wie man spezielle Admin REST-Services wie einen health-check hinzufügt und wie man ein JAR oder WAR bauen kann.

Einführung

spring-boot-project-logoSpring Boot ist Teil des Spring Frameworks und ermöglicht eine sehr schnelle Entwicklung von Spring Anwendungen. Mit Spring Boot können Web- und standalone Anwendungen gebaut werden. Das Ziel von Spring Boot ist, eine Anwendung möglichst schnell zu entwickeln und laufen zu lassen. Dafür wird das Paradigma convention over configuration verwendet, alle notwendigen Bibliotheken werden bereits mitgebracht und auch ein Application Server ist embedded schon dabei.
Im günstigsten Fall wird ein Maven Projekt mit sehr wenigen dependencies aufgesetzt und direkt der Anwendungscode entwickelt. Man muss sich nicht um notwendige Bibliotheken kümmern, es ist keine Konfiguration notwendig und auch das Einrichten eines Application Servers sowie das Deployment entfällt. Nach dem Maven build hat man ein jar-File, das man startet und schon läuft die Web-Anwendung. Diese kann man standalone auf dem Rechner oder auf einem Server ausführen.
Somit ist Spring Boot auch eine ideale Sache, wenn man ein und dieselbe Anwendung lokal offline und zentral auf einem Server bereit stellen möchte.

Das Ergebnis ist also ein File, in dem alles drin ist, was man zum Ausführen benötigt und das für sich isoliert ausgeführt werden kann. Gute Voraussetzungen für Microservices.

Der Spring Guide Getting Started gibt einen schönen knappen Einstieg in Spring Boot. In diesem Artikel werde ich einen IBAN Konverter Microservice entwickeln.
Die Sourcen des Beispiels stehen unter GitHub bereit, das Ergebnis in der online Demo.

Projekt Setup mit Maven

Für einen REST-Service brauchen wir ein Web-Projekt. Dazu wird ein normales Java Maven Projekt angelegt und eine pom.xml erstellt, die eine dependency zum starter-web von Spring Boot hat. Damit der Build mit Maven einfacher geht wird noch das Spring Boot Maven Plugin eingebunden.

Die pom.xml von unserem IBAN-Konverter Microservice sieht wie folgt aus:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>net.marcomichel.labor.springboot</groupId>
	<artifactId>Spring-Boot-Microservice</artifactId>
	<version>0.0.1</version>
	<packaging>jar</packaging>

	<name>Spring-Boot-Microservice</name>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.5.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
	<description>Test Entwicklung Microservice mit Spring Boot</description>
</project>

Jetzt ist noch eine Java Klasse mit einer main-Methode notwendig die als Start-Klasse von Spring Boot dient. Diese startet die Spring Boot Anwendung und initialisiert den Spring Kontext.

package net.marcomichel.labor.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import net.marcomichel.labor.ibankonverter.boundary.IbanKonverter;
import net.marcomichel.labor.ibankonverter.control.IbanKonverterImpl;

/**
 * Startklasse für den IBAN REST Service
 */
@SpringBootApplication
public class IbanApplication {

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

Erstellen des REST-Service

Für den IBAN-Konverter REST Service benötigen wir eine Java Klasse mit einer Methode zum Konvertieren von Kontonummer und Bankleitzahl in eine IBAN Nummer. Desweiteren benötigen wir einen REST-Controller, der den REST-Service-Call entgegen nimmt, die IBAN-Konverter Funktionalität aufruft und die IBAN-Nummer als JSON-Objekt zurück gibt.

Die Funktionalität zum konvertieren befindet sich in einer POJO, die ein Interface implementiert. Damit ist die Kernfunktionalität unabhängig von einer bestimmten Technologie oder einem Framework wie Spring. Dieses Best Practice ermöglicht es, dass die Kernfunktionalität mit verschiedenen Java Technologien verwendet werden kann, ohne dass sie umprogrammiert werden muss. Spring muss aber wissen, dass es diese POJO gibt. Dafür wird sie als Bean konfiguriert. Um die Konfiguration einfach und kompakt zu halten, erfolgt die Konfiguration nicht in einem XML-File, sondern mit Java Code. Dazu wird die Starter Klasse unserer Anwendung (IbanApplication.java) um folgende Methode ergänzt:

	@Bean
	public IbanKonverter ibanKonverter() {
		return new IbanKonverterImpl();
	}

Die Annotation @Bean gibt an, dass mit dieser Methode eine Bean definiert wird. Die Methode gibt eine Instanz der Bean zurück. Damit ist Spring die Java Klasse bekannt und sie wird von Spring instanziiert. Im REST-Controller werden wir sehen, wie die Bean per Dependency Injection von Spring injiziert wird.

Der REST-Controller sieht wie folgt aus:

package net.marcomichel.labor.springboot;

import java.util.Collections;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import net.marcomichel.labor.ibankonverter.boundary.IbanKonverter;

/**
 * IBAN Konverter REST Controller
 */
@RestController
public class IbanController
{
	@Autowired
	private IbanKonverter konverter;

	@RequestMapping(value="/iban", method=RequestMethod.GET)
	public Map<String, String> iban(@RequestParam(value="blz") String blz, @RequestParam(value="kto") String kto) {
		final String iban = konverter.berechneIbanDE(blz, kto);
		return Collections.singletonMap("iban", iban);
	}

}

Die Annotation @RestController deklariert diese Klasse als einen REST-Controller.
Über @Autowird wird dem Controller eine Instanz des IBAN-Konverters injiziert. Das ist das normale Spring Dependency Injection Verfahren.
Die Methode iban(String, String) wird automatisch aufgerufen, sobald ein Request beim REST-Service ankommt. Sie bekommt die Bankleitzahl und die Kontonummer als Aufruf-Parameter übergeben.
Über die Annotation @RequestParam wird festgelegt, welcher Request-Parameter auf welchen Methoden-Parameter gemappt wird. Dazu wird der Name des Request-Parameters angegeben.
Die Annotation @RequestMapping legt fest über welchen Pfad der REST-Service aufgerufen wird und welche HTTP-Methode akzeptiert wird. In unserem Fall akzeptieren wir einen GET-Request der über die relative URL „/iban“ aufgerufen wird. Die URL ist relativ zur Server-URL.
Der Rückgabewert der Methode wird automatisch von Spring in ein JSON Objekt transformiert. Die automatische Transformation funktioniert aber nicht bei simplen Datentypen wie einem String. Deshalb wird der String mit der IBAN-Nummer in eine Map konvertiert. Der Key ist der Name des JSON-Attributs, der Value der Map ist der Value des JSON-Objekts.

Damit ist unser IBAN REST-Service schon fertig. Über einen Maven build (package oder install) wird das jar-Fie mit dem Microservice gebaut.

Ausführen des Microservice

In einer Entwicklungsumgebung wie Eclipse kann die Spring-Boot Anwendung einfach über Run ausgeführt werden. Man muss lediglich die Starter-Klasse mit der main-Methode (bei uns IbanApplication.java) ausführen. Spring Boot startet den integrierten Tomcat und die Anwendung. In der Konsole sind die Meldungen des Startvorgangs zu sehen. Am Ende des Startvorgangs steht die Meldung „Started IbanApplication in 14.791 seconds (JVM running for 15.911)„. Jetzt kann der REST-Service z.B. über http://localhost:8080/iban?kto=123&blz=456 aufgerufen werden.

Will man die Anwendung außerhalb der Entwicklungsumgebung starten muss lediglich das jar-File mit dem Befehl „java -jar {Name des JAR-Files}“ in der Konsole ausgeführt werden. Spring Boot startet wieder den integrierten Tomcat und der REST-Service ist wieder unter localhost:8080 aufrufbar.
Möchte man einen anderen Port verwenden kann dies beim Starten, mit dem Parameter „-Dserver.port„, mitgegeben werden. Um die Anwendung unter dem Port 9090 laufen zu lassen ist folgender Befehl notwendig: „java -Dserver.port=9090 -jar {Name des JAR-Files}„.

Um den REST-Service im Internet verfügbar zu machen, wird das JAR-File ganz einfach auf einen Server kopiert und wie eben beschrieben gestartet. Wird der Start-Befehl in einem Start-Script des Servers eingebunden startet der REST-Service mit dem Server Start. Einfacher geht es kaum. File kopieren und starten. Kein Einrichten eines Application Servers, kein Deployment im Application Server. Es muss nur eine Java Runtime auf dem Server vorhanden sein.
Es gibt einen Weg, der noch einfacher ist. Das JAR-File in einer Cloud-Plattform wie Cloudfoundry ausführen. Wie das geht zeige ich in einem späteren Artikel.

UI und statischer Web-Content

Jetzt bekommt unser Microservice noch ein UI. Wir erstellen ein JavaScript UI, das den REST Service verwendet. Dazu müssen wir die Spring Boot Anwendung um statische Files ergänzen (HTML-, CSS- und JavaScript-File). Die Files werden im Maven-Projekt im Ordner src/main/resources/static abgelegt. Spring Boot nutzt diesen Ordner standardmäßig als Root der Web-Anwendung und stellt alle darin enthaltenen Files als Web Content zur Verfügung.
Das UI des IBAN Konverters habe ich mit AngularJS Und Bootstrap gebaut. In diesem Artikel möchte ich nicht auf die Details eingehen und auf den Artikel AngularJS – next generation JavaScript  verweisen.

Admin Services

Für eine produktive Anwendung brauchen wir noch die Möglichkeit die Anwendung zu monitoren. Spring Boot stellt dafür diverse Admin Services, z.B. einen health-Check, als REST-Services bereit. Um diese Actuator Module zu nutzen muss lediglich die pom.xml um eine Dependency erweitert werden.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Nach dem Restart der Anwendung kann über http://localhost:8080/health der „Gesundheitszustand“ der Anwendung abgefragt werden. Neben health gibt es noch errors, environment, beans, info, metrics, trace, configprops, und dump. Details können der Spring Doku entnommen werden.

Build eines war-Files

Bisher haben wir ein jar-File beim Build erzeugt. Es ist aber auch möglich ein war-File zu erzeugen. Dies ist dann sinnvoll, wenn man nicht den embedded Tomcat nutzen und die Anwendung in einen separaten Application Server deployen möchte.

Um ein war-File zu erzeugen muss in der pom.xml das packaging auf war geändert

<packaging>war</packaging>

und der embedded Tomcat als provided deklariert werden.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-tomcat</artifactId>
   <scope>provided</scope>
</dependency>

Auch diese Option ist in der Spring Doku beschrieben.

Fazit

Wir haben sehr schnell einen Microservice entwickelt, ohne uns mit Infrastruktur, Application Server, Infrastruktur- bzw. glue-Code oder Konfiguration rum schlagen zu müssen. Das Versprechen von Spring Boot „up and running very fast“ trifft zu. Das Ergebnis des Maven Build ist ein Fat-JAR, das wir direkt ausführen können und das alles enthält, was wir benötigen.
Das ist allerdings auch die Kehrseite der Medaille. Das jar-File des IBAN-Konverters hat eine Größe von 13,6 MB. Das ist jetzt noch nicht so schlimm, es enthält ja einen Application Server. Wenn mann aber mal rein schaut, sieht man, dass viele Bibliotheken enthalten sind, die wir in unserem Anwendungsfall nicht direkt benötigen. Ich weiss nicht, welche Spring Boot selbst benötigt und würde deshalb nicht anfangen über Maven-Excludes Bibliotheken wieder raus zu werfen.
Ich sehe viel mehr den Vorteil der schnellen Entwicklung, insbesondere für das Rapid Prototyping. Ob man eine Spring-Boot Anwendung auch produktiv betreiben möchte, muss jeder für sich selbst entscheiden.

One Reply to “Microservices mit Spring-Boot”

  1. Pingback: Spring-Boot-Microservice auf Cloud Foundry deployen | discoveration

Schreibe einen Kommentar

Anmelden um einen Kommentar abzugeben.

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*