Overview

Skill Level: Intermediate

This article describes some key features required to develop a cloud native application using Thorntail framework. This article is reviewed by Chinmohan Biswas(Chief Architect) and Anirban Mukherjee(Lead Architect).

Ingredients

microprofile-logo-w-tagline                                     thorntail-logo                                                    index

Thorntail implements Eclipse MicroProfile, which is a community-driven open source specification that optimizes Enterprise Java for a microservices architecture and delivers application portability across multiple MicroProfile runtimes. Red Hat OpenShift is a Kubernetes-based container application development and hosting platform .

Prequisites:

1>Windows 7 or latter.

2>Java version: 1.8.0_201

3>Apache Maven 3.5.2

4>Eclipse

5>For minishift installation in windows , the following link can be followed.

https://www.marksei.com/openshift-minishift-widnows/.

Alternatively, a remote OpenShift account can be used.

Step-by-step

  1. Create a project in eclipse

    proj-structureProject Structure

  2. Create required maven dependencies

    Thorntail consists of unbounded set of capabilities called fractions.For each fraction , we have to add maven dependency.

    a>Dependency for jaxrs

    <dependency>
    <groupId>io.thorntail</groupId>
    <artifactId>jaxrs</artifactId>
    </dependency>

    b>Dependency for external configuration

    <dependency>
    <groupId>io.thorntail</groupId>
    <artifactId>jaxrs</artifactId>
    </dependency>

    c>Dependency for fault tolerance

    <dependency>
    <groupId>io.thorntail</groupId>
    <artifactId>microprofile-fault-tolerance</artifactId>
    </dependency>

    d>Dependency for JWT

    <dependency>
    <groupId>io.thorntail</groupId>
    <artifactId>microprofile-jwt</artifactId>
    </dependency>

    e>Dependency for open api

    <dependency>
    <groupId>io.thorntail</groupId>
    <artifactId>microprofile-config</artifactId>
    </dependency>

    f>Dependency for metrics

    <dependency>
    <groupId>io.thorntail</groupId>
    <artifactId>microprofile-metrics</artifactId>
    </dependency>

    g>Dependency for open tracing

    <dependency>
    <groupId>io.thorntail</groupId>
    <artifactId>microprofile-opentracing</artifactId>
    </dependency>

    h>Dependencies for JPA

    <dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>javax.persistence-api</artifactId>
    <version>2.2</version>
    </dependency>
    <dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>javax.persistence</artifactId>
    <version>2.2.1</version>
    </dependency>

    i>Dependencies for postgresssql

    <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>9.4-1200-jdbc41</version>
    </dependency>

    For getting common version of all thorntail versions, we used the following dependency management:

    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>io.thorntail</groupId>
    <artifactId>bom</artifactId>
    <version>${version.thorntail}</version>
    <scope>import</scope>
    <type>pom</type>
    </dependency>
    </dependencies>
    </dependencyManagement>

    Mention the following in properties section:

    <version.thorntail>2.2.0.Final-redhat-00021</version.thorntail>

     

    The following build plugins are required:

    <plugins>
    <plugin>
    <groupId>io.thorntail</groupId>
    <artifactId>thorntail-maven-plugin</artifactId>
    <version>${version.thorntail}</version>
    <executions>
    <execution>
    <goals>
    <goal>package</goal>
    </goals>
    </execution>
    </executions>
    </plugin>

    <plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>${maven-war-plugin.version}</version>
    <configuration>
    <failOnMissingWebXml>false</failOnMissingWebXml>
    </configuration>
    </plugin>
    </plugins>

    For deploying in openshift , the following profile is required:

    <profile>
    <id>openshift</id>
    <build>
    <plugins>
    <plugin>
    <groupId>io.fabric8</groupId>
    <artifactId>fabric8-maven-plugin</artifactId>
    <version>${fabric8-maven-plugin.version}</version>
    <executions>
    <execution>
    <goals>
    <goal>resource</goal>
    <goal>build</goal>
    </goals>
    </execution>
    </executions>
    <configuration>
    <generator>
    <includes>
    <include>thorntail-v2</include>
    </includes>
    <excludes>
    <exclude>webapp</exclude>
    </excludes>
    </generator>
    </configuration>
    </plugin>
    </plugins>
    </build>
    </profile>

     

     

  3. Create rest controller and application service

    Create a rest controller with rest endpoints to intercept rest requests.

    Create a application service class. This class interacts with resources like database , queues , topics etc. In this poc we have taken a simple step of reading pre-inserted values from a table in postgresql. If we use JPA as ORM framework , then we have to create a persistence.xml which contains the database connection details. The path of persistence.xml is “src/main/resources/META-INF/persistence.xml”.

    Microprofile Context and Dependency injection allows developers to bind the objects to their well-defined contexts. We defined our application service with “@ApplicationScoped” and injected in rest controller wih “@Inject”.

  4. Ensure Fault Tolerance

    MicroProfile provides the following mechanisms to ensure fault tolerance:

    Circuit breaker , Fallback  , Retry , Bulkhead

    1>Circuit Breaker Utility : Remote invocations can hang without a response until some timeout limit is reached. If a service becomes unresponsive and we have multiple clients invoking that service , then we may run out of critical compute resources leading to cascading failures in multiple areas. If we find a service has problems, a circuit breaker can be used to force calls to fail immediately and stop subsequent invocations of that service. A service endpoint can be wrapped with a circuit breaker object. The circuit breaker object then monitors that endpoint  Circuit breaker has following states:

    a>Closed: Monitored endpoint is working as expected and all calls flow through that service endpoint.

    b>Open: Error is returned and monitored endpoint is not executed.

    c>Half-Open : After a timeout period, the circuit switches to a half-open state to test if the underlying problem still exists. If a single call fails in this half-open state, the breaker is once again tripped. If it succeeds, the circuit breaker resets back to the normal, closed state. 

    Let us see the following example

    @CircuitBreaker(requestVolumeThreshold = 4, failureRatio=0.75, delay = 1000, successThreshold = 10)

    This meas if 75% requests fail in a rolling window of 4 requests , then the circuit will be in open state and the monitored endpoint is not executed for 1000ms. After the 1000ms delay, the circuit is placed to half-open. At this point, trial calls will probe the destination and after 10 consecutive successes, the circuit will be placed back to closed 

    2>Fallback : Provides alternative execution path , in case of failures.

    3>Retry : Specifies the number of times a specific endpoint will be retried in case of failures.

    4>Bulkhead: Limits the number of concurrent requests.

    All fault tolerance annotations can be combined as follows to monitor an endpoint-

    @CircuitBreaker(requestVolumeThreshold = 4, failureRatio = 0.75, delay = 1000, successThreshold = 10, )
    @Retry(retryOn = {RuntimeException.class, TimeoutException.class}, maxRetries = 2)
    @Timeout(500)
    @Fallback(fallbackMethod = “greetingFallback”)

     

  5. Configurability

    Configuration values can be simply injected by using the @ConfigProperty annotation . Example:

    @Inject
    @ConfigProperty(name = “db.username”)
    private String dbUserName;

    @Inject
    @ConfigProperty(name = “db.password”)
    private String dbPassword;

    We have to declare the following key/value pairs in microprofile-config.properties.

     The property value can be read from a property file or from a ConfigMap created as a resource. We will discuss how to create a config map in step10.

  6. Login to openshift

    OpenShift Container Platform is based on Kubernetes which is the most used Orchestration for containers running in production.

    We can run minishift locally by using the below command:

    minishift start –vm-driver virtualbox

    If we have a remote OpnenShift account , we can directly login to that by using the below command

    oc login {host}

     

    This command will interactively ask for host , username , password.

     

     

  7. Create a new project in OpenShift

    Create a new project using the below command

    oc new-project {projectname}

    For this poc we have done

    oc login https://console.starter-us-west-2.openshift.com

  8. Create a Postgres database in OpenShift

    Login to OpenShift web console and navigate to the newly created project. Click on “Add to Project” and select “PostgreSQL” from catalog. Configure the service with the below properties : database name , user name , password. For POC, I created as below:

    PostgreSQL Database Name : sampledb,PostgreSQL Connection Username:developer,PostgreSQL Connection Password:developer.

    Once the service is created , we can navigate to overview menu and see that postgresql is running in a pod.

    postgresql-pod

    Navigate to  application->services and we have to keep note of the cluster ip and port.

    postgresql-service

    We have to navigate to the pod terminal of postgresql  and we used the following command to login :

    psql -h 172.30.9.14 -p 5432 -U developer sampledb. This command wil interactively ask for password.

    We have to noe create a table and insert some records. In our POC , I used the below:

    CREATE TABLE greeting (id serial PRIMARY KEY, greeting varchar);
    INSERT INTO greeting (greeting) VALUES (‘hello’);
    INSERT INTO greeting (greeting) VALUES (‘good morning’);
    INSERT INTO greeting (greeting) VALUES (‘good night’);
    INSERT INTO greeting (greeting) VALUES (‘good noon’);
    INSERT INTO greeting (greeting) VALUES (‘happy weekend’);

    sql-results

    Now our PostgreSql is ready in OpenShift

  9. Deploy the microservice application in OpenShift

    We can use the following maven command to deploy our application in OpenShift:

    mvn fabric8:deploy -Popenshift -DskiptTests=true

    This command uses the Fabric8 Maven Plugin to launch the S2I process on OpenShift and to start the pod. Source-to-Image (S2I) is a build tool for generating reproducible Docker-formatted container images from application sources. Once the build is complete we will see the following in the console:

    build-complete

    The following directories and files are created automatically in target folder:

     

    generated-files

    throntail-poc-thorntail.jar is Uberjar which contains everything we need to run our applications. It consists of application code , application , dependencies as well as server runtime.

    Once build is complete, deployment will start

    deployment-running

    Once the deployment is complete we can navigate to overview menu and and can find the below

     

    deployment-complete

    We can navigate to Applications -> Deployments . All the deployments will be displayed here.

    deployments

    We can select a deployment and see the details like metrics , logs

    metrics

     

     

     

  10. Create ConfigMap in OpenShift

    We can create a config map with the below command

    oc create configmap poc-config –from-literal=office=IBM –from-literal=profession=architecture

    We can now visit OpenShift console and navigate to Resources ->Config Maps

    All the config maps are displayed. We can select a config map and view its details. We can also edit the details of a config map.

    We have to now associate the config map with the deployed application. Click on “Add To Application” and do as below:

    config-map

    We have to get the config values in application code and we have to to the below:

    @Inject
    @ConfigProperty(name = “office”)
    private Optional<String> office;

    @Inject
    @ConfigProperty(name = “profession”)
    private Optional<String> profession;

    In this POC the key/value pairs do not have any business significance , these are for demonstration purpose .

  11. Test the deployed application

    Append the resource path with route url and ivoke from a browser

    test-result

    Values from config map in logs

    config-value-print

    We can get the metrics from the below url:

    http://throntail-poc-thorntail-poc.7e14.starter-us-west-2.openshiftapps.com/metrics

    We can get the health status with the below url:

    http://throntail-poc-thorntail-poc.7e14.starter-us-west-2.openshiftapps.com/health

     

     

     

     

  12. Conclusion

    Poc Code : https://github.com/debajyoti1d1mukherjee/Thorntail

     

     

Join The Discussion