Die 2016 gegründete und inzwischen in der Eclipse Foundation beheimatete Initiative MicroProfile ist angetreten, die Lücke zwischen dem Enterprise-Java-Standard (Java EE aka EE4J) und den Praxisanforderungen Microservices-basierter Architekturen zu schließen. Das bestehende Momentum der JEE-Community als Hebel nutzen und organisch um den Bedarf der Microservices-Community ergänzen, so der Plan. Und dieser Plan scheint aufzugehen. In nur wenigen Monaten ist es gelungen, eine Reihe sinnvoller Microservices-relevanter APIs mit bestehenden Java-EE-7/8-APIs zu kombinieren und diese in regelmäßigen MicroProfile-Releases zu veröffentlichen. Egal ob Health Check, Metrics, Fault Tolerance, JWT Propagation, Configuration, Tracing oder Open API, MicroProfile scheint die richtigen Antworten – sprich APIs – im Gepäck zu haben. Die Session zeigt den aktuellen Stand von MicroProfile und demonstriert dessen Mehrwert anhand praktischer Beispiele.
3. #WISSENTEILEN
ÜBER MICH
Wer bin ich - und wenn ja, wie viele?
• CIO New Technologies
• Enterprise & Mobile
• Autor, Speaker, Coach & Mentor
• Snowboard & MTB Enthusiast
Lars Röwekamp (a.k.a. @mobileLarson)
LR
10. #WISSENTEILEN
„Max cohesion, min coupling.“
JEE Business Component
Boundary Entity Control Pattern
je Service ein eigenes WAR
Kommunikation via REST
20. #WISSENTEILEN
„Just enough Server“
• Self-Contained Java Microservices (.jar)
• Java EE Komponenten on demand
• Embedded Servlet / Web Engine
• more to come …
Das geht mit Java EE?
JEE
22. #WISSENTEILEN
Die Mission:
„An open forum to optimize Enterprise Java
for a microservices architecture by innovating
across multiple implementations and
collaborating on common areas of interest
with a goal of standardization.“
MicroProfile
23. #WISSENTEILEN
Brücke zwischen Community und Standard
• breite Kooperation von Herstellern
• Portabel zwischen Implementierungen
• Förderung von Experimentierfreudigkeit
• Risikominimierung
• zügige Innovationen
MicroProfile
24. #WISSENTEILEN
Der Weg in die MicroProfile Zukunft:
• Schritt 1: Den „Hebel“ Java EE nutzen
• Schritt 2: Organische Innovationen
• Schritt 3: Zusammenarbeit via Open Source
MicroProfile
(*aktuelle Planung: 2-4 Releases/Jahr)
29. #WISSENTEILEN
Was brauche ich mindestens um einen
Microservice auf Basis von Java EE APIs
implementieren zu können?
• JAX-RS 2.0
• CDI 1.2
• JSON-P 1.0
MicroProfile
32. #WISSENTEILEN
Wie kann ich auf einheitliche Art und Weise
in meinen Services unterschiedlichste
Konfigurationsmechanismen nutzen?
• MicroProfile Config 1.2
MicroProfile
35. #WISSENTEILEN
// inject the WHOLE configuration
@Inject
Config config;
// inject a single configuration property of type String with default value
// the property MUST exist in one of the config sources, otherwise a DeploymentException will be thrwon
@Inject
@ConfigProperty(name = "VERSION")
String version;
// inject a single configuration property of type Boolean with default value
@Inject
@ConfigProperty(name = "DEBUG", defaultValue = "no")
boolean debug;
// inject a single configuration property of custom typ using a Converter<Person> converter
@Inject
@ConfigProperty(name = "PERSON")
Optional<Person> person;
36. #WISSENTEILEN
MicroProfile Config 1.2
• Default ConfigSources*
System Properties, Env Vars & Config File
• Custom ConfigSources*
z.B. DB oder ConfigServer
• Startup vs. Laufzeit
ConfigProperty vs Provider
MicroProfile
*inkl. ordinal
38. #WISSENTEILEN
Wie kann ich auf einheitliche Art und Weise
den Health Status und weitere Metriken
meines Microservices abfragen?
• MicroProfile HealthCheck 1.0
• MicroProfile Metrics 1.1
MicroProfile
39. #WISSENTEILEN
MicroProfile HealthCheck 1.0
• M2M, e.g Kuberentes Liveness & Rediness
• terminate, shutdown, replace?
• Single HC Endpoint pro MP Runtime (/health)
• logisches UND (gesund, wenn alle gesund)
• REST/HTTP (andere optional z.B. JMX)
MicroProfile
40. #WISSENTEILEN
// simplest health check possible
@Health
@ApplicationScoped
public class AliveHealthCheck implements HealthCheck {
// health check implementation
public HealthCheckResponse call() {
return HealthCheckResponse
.named("alive")
.withData("alive status","totaly alive" )
.state(true)
.build();
}
}
42. #WISSENTEILEN
MicroProfile Metrics 1.1
• deutlich mehr als nur UP/DOWN Info für
Scopes „base“, „vendor“, „application“
• Datentrends für Kapazitätsplanung
• Auslastung für automatisches Scalling
• proaktives Feststellen von Problemen*
MicroProfile
(z.B. Disk-Usage Anomalie)
43. #WISSENTEILEN
MicroProfile Metrics 1.1
• Pflicht- und Optional-Metrics
• Tags via Metric Description & Config API
• Metadaten (unit, type, description, …)
• Resuable Metrics (default ist false)
• JSON & Prometheus Format (mehr geplant)
MicroProfile
44. #WISSENTEILEN
// RESTful method with metrics annotation for measuring time consumption
// metric scope: application
// metric name: microprofile.demo.getAllCustomer
@GET
@Timed(absolute = true,
name = "microprofile.demo.getAllCustomers",
displayName = "customer retrieval time",
description = "Time of retrieval for all customers in ns",
unit = MetricUnits.NANOSECONDS
)
public Response getAllCustomers() {
// do some time consuming work
List customers = customerRepository.findAll();
// do some addtional work
...
return Response.ok(customers).build();
}
48. #WISSENTEILEN
Wie kann ich mein System so aufbauen,
dass es auch bei temporäre Ausfälle und
Problemsituation einzelner Microservices
stabil läuft (a.k.a. Resilient)?
• MicroProfile Fault Tolerance 1.0
MicroProfile
49. #WISSENTEILEN
MicroProfile Fault Tolerance 1.0
• Business Logik um Fehlertoleranz erweitern
• Trennung der Logik von deren Ausführung
• Ausführung erlaubt Resilience Patterns
• CDI Interceptor als Mittel zum Zweck
• Hystrix/Failsafe als Vorbilder für Standard API
MicroProfile
52. #WISSENTEILEN
// The configured the max retries is 10 but the max duration is 500ms.
// Once the duration is reached, no more retries should be performed
// even through it has not reached the max retries.
@Retry(delay=100, maxRetries=10, maxDuration=500)
private String doSomething () {
return … ;
}
// Retry will be performed on IOException.
@Retry(retryOn={IOException.class})
private String doSomething () {
return … ;
}
53. #WISSENTEILEN
// Reads all customers from DB based repository.
// In case of timeout the customers will be taken from cache
@Timeout(value = 200, unit = ChronoUnit.MILLIS) // timeout is 200ms
@Fallback(fallbackMethod = "readAllCustomerFromCache")
private List<Customer> readAllCustomersFromRepository() {
return customerRepository.findAll();
}
// Reads all customers from cache.
private List<Customer> readAllCustomerFromCache() {
return customerCache.findAll();
return customerCache;
}
57. #WISSENTEILEN
// Open circuit when once 3 (4x0.75) failures occur among the rolling
// window of 4 consecutive invocation. The circuit will stay open for
// 1000ms and then back to half open. After 10 consecutive successful
// invocations, the circuit will be back to close again.
@CircuitBreaker(successThreshold = 10,
requestVolumeThreshold = 4,
failureRatio=0.75,
delay = 1000)
@Fallback(fallbackMethod = “someFallbackMethod")
private String doSomething () {
return … ;
}
63. #WISSENTEILEN
Wie sichere ich meine Microservices gegen
unbefugten Zugriff ab OHNE dass bei jedem
Service eine neue Authentifizierung und
Authorisierung stattfinden muss?
• MicroProfile JWT Auth 1.0
MicroProfile
73. #WISSENTEILEN
// Step 1: mark REST API as „requiring MP-JWT Access Control“
@LoginConfig(authMethod = "MP-JWT", realmName = "TCK-MP-JWT")
@ApplicationPath("api") Requiring MP-JWT Access Control
public class ApiApplication extends Application {
...
}
74. #WISSENTEILEN
// Step 2: Inject and use json web token or parts (claims) of it
@Path("/customers")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public class CustomerResource {
@Inject
private JsonWebToken callerPrincipal;
@Inject
@Claim(value=“iat", standard=Claims.iat)
private Long issuedAtClaim;
...
}
75. #WISSENTEILEN
// Step 3: Add role based acces control for REST Endpoints
@DenyAll
@Path("/customers")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public class CustomerResource {
...
@GET
@RolesAllowed("admin")
public Response getAllCustomers() { ... }
@GET
@Path("/{id}")
@RolesAllowed({"user", "admin"})
public Response getCustomer(@PathParam("id") final UUID id) { ...}
}
80. #WISSENTEILEN
MicroProfile Open Tracing 1.0
• verteiltes Tracing über Servicgrenzen hinweg
• Correlation ID als gemeinsamer Nenner
• OpenTracing Standard als Basis
• Achtung: KEIN eigener Tracing Service
sondern lediglich dessen Support
MicroProfile
81. #WISSENTEILEN
// Tracing is enabled by default for all JAX-RS methods.
// You „only“ have to provide a distributed tracing service, e.g. Zipkin
82. #WISSENTEILEN
// Tracing is enabled by default for all JAX-RS methods.
// You „only“ have to provide a distributed tracing service, e.g. Zipkin
// explicitely disable tracing for a single method
@Traced(false)
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getCustomer(@PathParam("id") final UUID customerId) {
try {
Customer customer = customerRepository.find(customerId);
return Response.status(Response.Status.OK).entity(customer).build();
} catch (CustomerNotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
86. #WISSENTEILEN
Wie kann ich meine Microservices APIs
so dokumentieren, dass sie von anderen
verstanden und genutzt werden können.
• MicroProfile Open API 1.0
MicroProfile
88. #WISSENTEILEN
MicroProfile Open API 1.0
• OpenAPI v3 Standard (abgeleitet von Swagger)
• Scan- und Annotationen-basiert (fast 50 @s)
• frei konfigurierbar (Scanpath etc.)
• mit YAML, JSON kombinierbar (in META-INF)
• Filter setzen via Config API
MicroProfile
89. #WISSENTEILEN
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Get user by user id")
@APIResponse(responseCode = "200",
description = "The user",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Customer.class)))
@APIResponse(responseCode = "404",
description = "User not found")
public Response getCustomer(
@Parameter(description = "The unique id that needs to be fetched. ", required = true)
@PathParam("id") final UUID id) {
try {
Customer customer = customerRepository.find(id);
return Response.status(Response.Status.OK).entity(customer).build();
} catch (CustomerNotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
90. #WISSENTEILEN
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Get user by user id")
@APIResponse(responseCode = "200",
description = "The user",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Customer.class)))
@APIResponse(responseCode = "404",
description = "User not found")
public Response getCustomer(
@Parameter(description = "The unique id that needs to be fetched. ", required = true)
@PathParam("id") final UUID id) {
try {
Customer customer = customerRepository.find(id);
return Response.status(Response.Status.OK).entity(customer).build();
} catch (CustomerNotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}