Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

A Hitchhiker's Guide to Cloud Native Java EE


Published on

JavaLand 2018, Brühl: Talk by Mario-Leander-Reimer (@LeanderReimer, Principal Software Architect at QAware)

Cloud-native applications are popular these days. They promise superior reliability and almost arbitrary scalability. They follow three key principles: They are built and composed as microservices. They are packaged and distributed in containers. The containers are executed dynamically in the cloud. But all this comes at a price: Added complexity! Suddenly you need to consider cloud-native design principles such as service discovery, configuration, resilience, health checks and diagnosability.

While current Java EE versions do not (yet) have dedicated APIs to fully address these principles, they do provide APIs and extension points to retrofit these concepts easily with only a few lines of glue code into your plain Java EE microservice.

This code intense session will present how we have built a fully cloud-native Java EE based system consisting of several microservices for a large German car manufacturer in only three months. We will share our experiences as well as working code examples on how we leveraged and combined standard Java EE APIs and well known open source components to
do the heavy cloud-native lifting.

Published in: Data & Analytics
  • Be the first to comment

A Hitchhiker's Guide to Cloud Native Java EE

  1. 1. A Hitchhiker's Guide to Cloud Native Java EE @LeanderReimer #CloudNativeNerd
  2. 2. Mario-Leander Reimer Chief Technologist, QAware GmbH Contact Details Mail: Twitter: @LeanderReimer Github: 12.03.2018 2 Developer && Architect 20+ years of experience #CloudNativeNerd Open Source Enthusiast
  3. 3. 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. 4
  4. 4. 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. 6. 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.
  7. 7. 8
  8. 8. 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
  9. 9. 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
  10. 10. 11 Do this quickly! Microservices as Refactoring.
  11. 11. Overview of Java EE 7 APIs. 12 CDI Extensions Web Fragments Bean Validation 1.1 CDI 1.1 Managed Beans 1.0 JCA 1.7 JPA 2.2JMS 2.0 JSP 2.3 EL 3.0 EJB 3.2 Batch 1.0 JSF 2.2 Interceptors 1.2 Mail 1.5 Common Annotations 1.3 JTA 1.2 JAX-WS 1.4 JAX-RS 2.0 Concurrency 1.0 JSON-P 1.0 WebSocket 1.1 JASPIC 1.1 JACC 1.5 Servlet 3.1 JCache 1.0
  12. 12. Overview of Java EE 8 APIs. CDI Extensions Web Fragments BeanValidation2.0 CDI 2.0 Managed Beans 1.0 JCA 1.7 JPA 2.2 JMS 2.0 JSP 2.3 EL 3.0 EJB 3.2 Batch 1.0 JSF 2.3 Interceptors 1.2 Mail 1.6 Common Annotations 1.3 JTA 1.2 JAX-WS 1.4 JAX-RS 2.1 Concurrency 1.0 JSON-P 1.1 JSON-B 1.0 WebSocket 1.1 JAPSIC 1.1 JACC 1.5 Security1.0 Servlet 4.0 JCache 1.0 13
  13. 13. Cloud-ready runtimes suited for Java EE microservices. 14 many more.
  14. 14. The Cloud-native Java EE showcase. 15 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
  15. 15. Communication
  16. 16. @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
  17. 17. 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
  18. 18. @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));, reader.readObject())); } } 19 Simple Message Driven Beans to receive messages. This also works for For other JCA adapters visit
  19. 19. 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.
  20. 20. Configuration
  21. 21. 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(); }
  22. 22. apiVersion: v1 kind: ConfigMap metadata: name: process-service-config data: APP_NAME: process-service org_apache_deltaspike_ProjectStage: Production | process.service.jwt.secret=some-secret ... | 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
  23. 23. Diagnosability
  24. 24. <!-- --> <servlet> <servlet-name>adminServlet</servlet-name> <servlet-class> com.codahale.metrics.servlets.AdminServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>adminServlet</servlet-name> <url-pattern>/admin/*</url-pattern> </servlet-mapping> 25 Retrofitting metrics, health and admin endpoints using the Dropwizard Metrics library in 30 minutes. <dependencies> <dependency> <groupId>io.dropwizard.metrics</groupId> <artifactId>metrics-core</artifactId> <version>${metrics.version}</version> </dependency> </dependencies> Usage of Dropwizard Metrics to retrofit metrics, health and admin endpoints Easy integration with any JEE7 application Definition of Custom Health Checks possible Used as Liveness und Readiness Probes dropwizard-metrics-to-prometheus/
  25. 25. # container will receive requests if probe succeeds readinessProbe: httpGet: path: /admin/healthcheck port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 # container will be killed if probe fails livenessProbe: httpGet: path: /admin/ping port: 8080 initialDelaySeconds: 30 timeoutSeconds: 5 Liveness and Readiness Probes for Antifragility. 26
  26. 26. Resiliency
  27. 27. 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></groupId> <artifactId>hystrix-core</artifactId> <version>${hystrix.version}</version> </dependency> </dependencies>
  28. 28. 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
  29. 29. Go MicroProfile.
  30. 30. Alternatively, just use the MicroProfile 1.2 APIs. 31
  31. 31. FROM payara/server-full:181 ENV AS_ADMIN $PAYARA_PATH/bin/asadmin ENV DOMAIN domain1 COPY build/activemq/activemq-rar-5.15.3.rar /tmp/ COPY build/postgresql/* ${PAYARA_PATH}/glassfish/domains/${PAYARA_DOMAIN}/lib/ COPY build/hazelcast/* ${PAYARA_PATH}/glassfish/lib/ COPY domain-config.asadmin /tmp/ RUN $AS_ADMIN start-domain $DOMAIN && $AS_ADMIN --user admin --passwordfile=/opt/pwdfile multimode --file /tmp/domain-config.asadmin && $AS_ADMIN stop-domain $DOMAIN COPY build/libs/process-service.war /opt/payara41/glassfish/domains/domain1/autodeploy/ Example Dockerfile for Payara Server. 33 The magic happens here.
  32. 32. deploy --type rar --name activemq-rar /tmp/activemq-rar-5.15.3.rar create-resource-adapter-config --property ServerUrl='tcp://message-queue:61616':UserName='admin':Password='admin' activemq-rar create-connector-connection-pool --raname activemq-rar --connectiondefinition javax.jms.ConnectionFactory --ping false --isconnectvalidatereq true jms/activeMqConnectionPool create-connector-resource --poolname jms/activeMqConnectionPool jms/activeMqConnectionFactory create-admin-object --raname activemq-rar --restype javax.jms.Queue --property PhysicalName=PROCESS.EVENTS jms/ProcessEvents create-admin-object --raname activemq-rar --restype javax.jms.Queue --property PhysicalName=BILLING.EVENTS jms/BillingEvents create-admin-object --raname activemq-rar --restype javax.jms.Queue --property PhysicalName=PAYMENT.EVENTS jms/PaymentEvents create-jdbc-connection-pool --datasourceclassname org.postgresql.ds.PGConnectionPoolDataSource --restype javax.sql.ConnectionPoolDataSource --property portNumber=5432:password='12qwasyx':user='process':serverName=process-db:databaseName='process' PostgresPool create-jdbc-resource --connectionpoolid PostgresPool jdbc/ProcessDb set resources.jdbc-connection-pool.PostgresPool.connection-validation-method=custom-validation set resources.jdbc-connection-pool.PostgresPool.validation-classname=org.glassfish.api.jdbc.validation.PostgresConnectionValidation set set create-managed-thread-factory concurrent/BackendThreadFactory Payara Server specific domain configuration. 34
  33. 33. 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
  34. 34. 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
  35. 35. 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
  36. 36. apiVersion: extensions/v1beta1 kind: Deployment metadata: name: process-service spec: replicas: 2 strategy: type: RollingUpdate template: metadata: labels: io.kompose.service: process-service hazelcast: enabled spec: containers: - name: process-service image: lreimer/process-service:1.1 ports: - containerPort: 8080 Example K8s Deployment Definition. 41
  37. 37. 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
  38. 38. -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.
  39. 39. #Cloudkoffer #Kubepad #Cloudcontrol Cloud-native Java EE on OpenShift in Action. Booth 618 (OG) #CloudNativeNerd
  40. 40. Fork me on Github.
  41. 41. Mario-Leander Reimer @LeanderReimer