This document provides an overview of developing cloud native Java applications. It discusses:
- Using microservices and containers to build distributed and scalable applications.
- Key principles of cloud native design like designing for distribution, resilience, and automation.
- Tools for building microservices like Java EE, Dropwizard Metrics, Hystrix, and MicroProfile.
- Techniques for configuration, communication, diagnostics, and resiliency when developing microservices.
- Examples of using technologies like Docker, Kubernetes, Payara Server, ActiveMQ, and PostgreSQL in a microservices architecture.
The document provides a comprehensive but concise introduction to developing cloud native applications using microservices and Java technologies.
2. Mario-Leander Reimer
Chief Technologist, QAware GmbH
Contact Details
Mail: mario-leander.reimer@qaware.de
Twitter: @LeanderReimer
Github: https://github.com/lreimer/cloud-native-javaee
12.03.2018
2
Developer && Architect
20+ years of experience
#CloudNativeNerd
Open Source Enthusiast
3.
4. Transform: extract a portion of the existing
functionality into a new and modern system.
Coexist: both systems coexist for some time. Calls
agains the old functionality are diverted.
Eliminate: old functionality will be removed from
legacy system once no more clients are using it.
Ideal for Web- and API-Monoliths.
Slightly problematic for Non-RESTful URLs.
Apply stepwise evolution to your existing systems and
Cloud-native reconstruction using the Strangler Pattern.
4https://martinfowler.com/bliki/StranglerApplication.html
5. High level system overview after the reconstruction.
5
Process
MQseries
OTP
APRIL
Payment
OpenShift
Billing
Payment
APRIL
UI
B&P
B2I EAI/SAP
Saferpay
OSMC
6. BUILT AND COMPOSED
AS MICROSERVICES
3KEYPRINCIPLES
6
CLOUD NATIVE APPLICATIONS
PACKAGED AND
DISTRIBUTED IN CONTAINERS
DYNAMICALLY
ORCHESTRATED
IN THE CLOUD
7. Essential Design Principles for Cloud Native Apps.
7
Design for Distribution: Containers; microservices; API driven development.
Design for Configuration: One image, multiple environments.
Design for Resiliency: Fault-tolerant and self-healing.
Design for Elasticity: Scales dynamically and reacts to stimuli.
Design for Delivery: Short roundtrips and automated provisioning.
Design for Performance: Responsive; concurrent; resource efficient.
Design for Automation: Automated Dev & Ops tasks.
Design for Diagnosability: Cluster-wide logs, metrics and traces.
9. 9
Components All Along the Software Lifecycle.
DESIGN
▪ Complexity unit
▪ Data integrity unit
▪ Coherent and cohesive
features unit
▪ Decoupled unit
RUN
▪ Release unit
▪ Deployment unit
▪ Runtime unit
(crash, slow-down, access)
▪ Scaling unit
n:1
BUILD
▪ Planning unit
▪ Team assignment unit
▪ Knowledge unit
▪ Development unit
▪ Integration unit
1:1
10. 10
Dev Components Ops Components?:1
System
Subsystems
Components
Services
Good starting point
DecompositionTrade-Offs
Microservices
Nanoservices
Macroservices
Monolith
+ More flexible to scale
+ Runtime isolation (crash, slow-
+ Independent releases, deployments, teams
+ Higher utilization possible
Distribution debt: Latency
Increasing infrastructure complexity
Increasing troubleshooting complexity
Increasing integration complexity
15. The Cloud-native Java EE showcase.
15https://github.com/lreimer/cloud-native-javaee
Billing
Service
Payment
Service
Process
Service
JAX-RS
JMS
JCache
Datagrid
Service
Payment Queue
Billing Queue
Process Queue
JAX-RS
JMS
JPA
JSON-P
JAX-RS
JMS
JPA
JSON-P
17. @Path("process")
@Produces(MediaType.APPLICATION_JSON)
public class ProcessResource {
@Resource ManagedExecutorService executorService;
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void process(@Suspended AsyncResponse response, JsonObject jsonObject) {
response.setTimeout(5, TimeUnit.SECONDS);
response.setTimeoutHandler((r) -> r.resume(Response.accepted().build()));
executorService.execute(() -> {
response.resume(Response.ok().build());
});
}
@GET
@Path("/{processId}/status")
public Response process(@PathParam("processId") String processId) {
JsonObject payload = Json.createObjectBuilder()
.add("processId", processId).build();
return Response.ok(payload).build();
}
Simple sync and async REST APIs with JAX-RS.
17
18. 18
Different messaging patterns for reliable, flexible and
asynchronous communication between microservices.
P1 C1Q1
Message Passing
P1
C1
Q1
Cn
Work Queue
P1
C1T1
CnTn
Publish/Subscribe
P1 C1
Q1
Q2
Remote Procedure Call
19. @MessageDriven(name = "ProcessEventMDB", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/ProcessEvents"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto_acknowledge"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "PROCESS.EVENTS"),
@ActivationConfigProperty(propertyName = "resourceAdapter", propertyValue = "activemq-rar"),
@ActivationConfigProperty(propertyName = "messageSelector",
propertyValue = "contentType = 'application/vnd.process.v1+json'")
})
public class ProcessEventMDB implements MessageListener {
@Inject private Event<ProcessEvent> processEvent;
@Override
public void onMessage(Message message) {
String eventType = getEventType(message);
String body = getBody(message);
if ((eventType != null) && (body != null)) {
JsonReader reader = Json.createReader(new StringReader(body));
processEvent.fire(ProcessEvent.from(eventType, reader.readObject()));
}
}
19
Simple Message Driven Beans to receive messages.
This also works for
For other JCA adapters visit https://github.com/payara/Cloud-Connectors
20. JsonObject currentWeather = Json.createObjectBuilder()
.add(“processId", “4711")
.add(“some", “content")
.build();
StringWriter payload = new StringWriter();
JsonWriter jsonWriter = Json.createWriter(payload);
jsonWriter.writeObject(currentWeather);
TextMessage msg = session.createTextMessage(payload.toString());
msg.setJMSType(“ProcessEvent");
msg.setStringProperty("contentType",
"application/vnd.process.v1+json");
@ActivationConfigProperty(propertyName = "messageSelector",
propertyValue = "(JMSType = ProcessEvent') AND
(contentType = 'application/vnd.process.v1+json‘)“)
JsonReader reader = Json.createReader(new StringReader(body));
JsonObject jsonObject = reader.readObject();
20
Use JSON-P to build your JsonObject and
JsonArray instances.
Use JSON-P to read JSON payloads.
Use JSON-P to traverse and access JSON
objects and arrays.
Since Java EE 8: JSON Pointers and JSON
Patch add even more flexibility.
Use Mime-Type versioning for your JSON
messages if required.
Use JMS message selectors to filter on
JMS type and content type.
Alternatively use flexible binary protocols
like ProtoBuf.
Use JSON as payload format for loose coupling. Use
JSON-P to implement tolerant reader pattern.
22. Use Apache DeltaSpike for some extra configuration
features as well as other CDI extension magic.
22
DeltaSpike consists of a number of portable CDI extensions that provide useful features for Java
application developers.
Set of ready-to-use modules, including a core module and a number of optional modules for providing
additional enterprise functionality to your applications.
Core module with type-safe project stages and powerful interface based configuration mechanism
Data and JPA module enhanced JPA experience with declarative queries, reducing boilerplate to a
minimum
Security module for intercept and security checking on method calls.
Test-Control module for writing CDI-based tests easily
@Configuration(prefix = "some.", cacheFor = 30, cacheUnit = TimeUnit.SECONDS)
public interface SomeConfiguration {
@ConfigProperty(name = "url")
String url();
@ConfigProperty(name = "timeout", defaultValue = "30000")
long timeout();
}
23. apiVersion: v1
kind: ConfigMap
metadata:
name: process-service-config
data:
APP_NAME: process-service
org_apache_deltaspike_ProjectStage: Production
application.properties: |
process.service.jwt.secret=some-secret
...
feature-togglz.properties: |
DO_SOME_STUFF=false
...
log4j2.xml: |
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="60">
<Appenders> ... </Appenders>
<Loggers> ... </Loggers>
</Configuration>
23
Use ConfigMaps, Secrets and Volumes to provide ENV
or file based configuration data to your deployments.
spec:
containers:
- name: process-service
image: lreimer/process-service:1.1
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: process-service-config
volumeMounts:
- mountPath: /process-service/data
name: process-service-config-vol
volumes:
- name: process-service-config-vol
configMap:
name: process-service-config
28. 28
Retrofitting resiliency using Netflix Hystrix is also easy.
Use Netflix Hystrix for the resilient (synchronous) call of any external system
Rock solid Circuit Breaker and Bulk Heading implementation
Easy integration with any JEE application
Can be used easily with JAX-RS Client API for REST Calls.
Use Interceptors or Decorators to apply HystricCommands.
Can be integrated with JSR 236 Concurrency API via HystrixConcurrencyStrategy
Integrates seemlessly with Dropwizard Metrics
<dependencies>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>${hystrix.version}</version>
</dependency>
</dependencies>
29. public class HystrixJsr236ConcurrencyStrategy extends HystrixConcurrencyStrategy {
private final Context context;
public HystrixConcurrencyStrategyJsr236() {
context = initialContext();
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize,
HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
ThreadFactory threadFactory = lookupManagedThreadFactory(threadPoolKey);
if (threadFactory != null) {
return new ThreadPoolExecutor(corePoolSize.get(), maximumPoolSize.get(), keepAliveTime.get(),
unit, workQueue, threadFactory);
} else {
LOGGER.warn("Fallback to Hystrix default thread pool executor.");
return super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
}
... // 20 lines more implementation
}
create-managed-thread-factory concurrent/BackendThreadFactory
29
35. version: ‘3’
services:
message-queue:
image: rmohr/activemq:5.14.3
expose:
- "61616" # the JMS port
process-db:
image: "postgres:9.6.3"
environment:
- POSTGRES_USER=process
- POSTGRES_PASSWORD=12qwasyx
ports:
- ”15432:5432”. # the JDBC port
A docker-compose.yml for building and running locally.
35
process-service:
build: ./microservices/process-service
image: lreimer/process-service:1.1
volumes:
- ...
expose:
- "5701" # the outbound Hazelcast port
- "54327" # the multicast Hazelcast port
ports:
- "18081:8080" # the HTTP endpoint
depends_on:
- message-queue
- process-db
36. Lightweight development workflow for short roundtrips,
low infrastructure complexity and local troubleshooting.
37
kompose
K8s and OpenShift
Deployment YAML
Dockerfile +
docker-compose.yml
Local Development Cloud Deployment
37.
38. Most important Kubernetes concepts.
40
Services are an abstraction for a logical
collection of pods.
Pods are the smallest unit of compute in
Kubernetes
Deployments are an abstraction used to
declare and update pods, RCs,
Replica Sets ensure that the desired number
of pod replicas are running
Labels are key/value pairs used to identify
Kubernetes resources
40. resources:
# Define resources to help K8S scheduler
# CPU is specified in units of cores
# Memory is specified in units of bytes
# required resources for a Pod to be started
requests:
memory: “196Mi"
cpu: "250m"
# the Pod will be restarted if limits are exceeded
limits:
memory: “512Mi"
cpu: "500m"
Define Resource Constraints carefully.
42
41. -XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
-server
-Xmx320m -Xss256k -XX:MaxMetaspaceSize=160m -XX:CompressedClassSpaceSize=32m
# Or use G1GC
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=1 -XX:+CMSParallelRemarkEnabled
# Use for small heaps on 64-bit VMs
-XX:+AggressiveOpts
-XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:+UseStringDeduplication
# optional
-XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary
Tune your JVM!
43
Since jdk8_131
Extra memory settings
GC tuning.
Fancy tuning.
Diagnostics.