Amazon AWS

Implementing Microservices on AWS

1. Introduction

This is an in-depth article related to the Microservices on AWS Cloud using spring framework components. Spring Framework helps in creating stand-alone and production-ready applications. Spring Framework features are Spring MVC, JPA, Spring Boot, Micro Containers, and Messaging. It is an open-source software framework used by developers for creating web applications and services. Developers use Java technology stack for creating web apps.

Amazon Web Services is based on the internet as a group of remote computing services that are together called a cloud computing platform. AWS is offered by Amazon.com. The most commonly used services are Amazon EC2 and Amazon S3. You can deploy microservices based applications on AWS.

Microservices architecture is based on loosely coupled principles. The loosely coupled principle helps in designing services for application components with lesser dependencies. The Microservices application has multiple units. A single unit can run alone and use APIs for connectivity. API gateways help in the authentication of services.

aws microservices
AWS MicroServices

2. AWS Microservices

2.1 Prerequisites

Java 7 or 8 is required on the linux, windows or mac operating system. Maven 3.6.1 is required for building the spring and hibernate application. AWS account is needed.

2.2 Download

You can download Java 8 can be downloaded from the Oracle web site . Apache Maven 3.6.1 can be downloaded from Apache site. Spring framework latest releases are available from the spring website. AWS account can be created on Amazon AWS site.

2.3 Setup

You can create an account on AWS and create an instance using AWS Linux AMI template. You can download the .pem file while inputing the key pair. You can save the .pem file as aws_ec2.pem. You can use the command below to ssh into aws console. You can use ec2-user account to login into the instance created. (ec2-35-154-183-212.ap-south-1.compute.amazonaws.com)

AWS Console Setup

ssh -i aws_ec2.pem ec2-user@ec2-35-154-183-212.ap-south-1.compute.amazonaws.com

You can set up the java by using the command below:

Java Setup

sudo yum install java-1.8.0-openjdk-devel
java -version
alternatives --config java

You can check the java setup by using the alternatives command mentioned above. You can set the environment variables for JAVA_HOME and PATH. They can be set as shown below:

Java Environment Setup

export JAVA_HOME="/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.302.b08-0.amzn2.0.1.x86_64"

 PATH=$JAVA_HOME/bin:$PATH

You can set up the maven by using the command below:

Maven Setup

sudo wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
 sudo sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo
 sudo yum install -y apache-maven
 mvn --version

The environment variables for maven are set as below:

Maven Environment Setup

JAVA_HOME=”/jboss/jdk1.8.0_73″
export M2_HOME=/users/bhagvan.kommadi/Desktop/apache-maven-3.6.1
export M2=$M2_HOME/bin
export PATH=$M2:$PATH

2.4 How to download and install Spring framework

Spring framework’s latest releases are available from the spring website. You can select the framework based on your operating system. After downloading the zip file can be extracted to a folder. The libraries in the libs folder are set in the CLASSPATH variable.

2.5 Building the Application

2.5.1 Spring

You can start building Spring applications using Spring Boot. Spring Boot has minimal configuration of Spring. Spring Boot has simplified security, tracing, application health management and runtime support for webservers. Spring configuration is done through maven pom.xml. The xml configuration is shown as below:

Spring Configuration

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>org.springframework</groupId>
    <artifactId>spring-helloworld</artifactId>
    <version>0.1.0</version>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
    </parent>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <properties>
        <java.version>1.8</java.version>
    </properties>
 
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
</project>

You can create a HelloWorldController class as the web controller. The class is annotated using @RestController. Rest Controller is used to handle requests in Spring Model View Controller framework. Annotation @RequestMapping is used to annotate the index() method. The code for the HelloWorldController class is shown below:

HelloWorldController

package helloworld;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
 
@RestController
public class HelloWorldController {
     
    @RequestMapping("/")
    public String index() {
        return "Hello World\n";
    }
     
}

HelloWorldApp is created as the Spring Boot web application. When the application starts, beans, ​and settings are wired up dynamically. They are applied to the application context. The code for HelloWorldApp class is shown below:HelloWorldApp

Run Command

package helloworld;
import java.util.Arrays;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
 
@SpringBootApplication
public class HelloWorldApp {
     
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(HelloWorldApp.class, args);
         
        System.out.println("Inspecting the beans");
         
        String[] beans = ctx.getBeanDefinitionNames();
        Arrays.sort(beans);
        for (String name : beans) {
            System.out.println("Bean Name" +name);
        }
    }
 
}

Maven is used for building the application. The command below builds the application.

Maven Build Command

mvn package

The output of the executed command is shown below.

Maven Output

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building spring-helloworld 0.1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ spring-helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/ec2-user/spring/src/main/resources
[INFO] skip non existing resourceDirectory /home/ec2-user/spring/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ spring-helloworld ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ spring-helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/ec2-user/spring/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ spring-helloworld ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ spring-helloworld ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running helloworld.HelloWorldControllerTest
21:34:42.040 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class helloworld.HelloWorldControllerTest]
21:34:42.054 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
21:34:42.078 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
21:34:42.140 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [helloworld.HelloWorldControllerTest] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
21:34:42.177 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [helloworld.HelloWorldControllerTest], using SpringBootContextLoader
21:34:42.195 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [helloworld.HelloWorldControllerTest]: class path resource [helloworld/HelloWorldControllerTest-context.xml] does not exist
21:34:42.196 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [helloworld.HelloWorldControllerTest]: class path resource [helloworld/HelloWorldControllerTestContext.groovy] does not exist
21:34:42.196 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [helloworld.HelloWorldControllerTest]: no resource found for suffixes {-context.xml, Context.groovy}.
21:34:42.198 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [helloworld.HelloWorldControllerTest]: HelloWorldControllerTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
21:34:42.348 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [helloworld.HelloWorldControllerTest]
21:34:42.491 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [/home/ec2-user/spring/target/classes/helloworld/HelloWorldApp.class]
21:34:42.507 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration helloworld.HelloWorldApp for test class helloworld.HelloWorldControllerTest
21:34:42.721 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [helloworld.HelloWorldControllerTest]: using defaults.
21:34:42.722 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
21:34:42.736 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Skipping candidate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener] due to a missing dependency. Specify custom listener classes or make the default listener classes and their required dependencies available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
21:34:42.737 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Skipping candidate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] due to a missing dependency. Specify custom listener classes or make the default listener classes and their required dependencies available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
21:34:42.739 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@72057ecf, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@1afd44cb, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@6973b51b, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@1ab3a8c8, org.springframework.test.context.support.DirtiesContextTestExecutionListener@43195e57, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@333291e3, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@479d31f3, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@40ef3420, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@498d318c, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@6e171cd7]
21:34:42.745 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [helloworld.HelloWorldControllerTest]
21:34:42.751 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [helloworld.HelloWorldControllerTest]
21:34:42.753 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [helloworld.HelloWorldControllerTest]
21:34:42.753 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [helloworld.HelloWorldControllerTest]
21:34:42.754 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [helloworld.HelloWorldControllerTest]
21:34:42.754 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [helloworld.HelloWorldControllerTest]
21:34:42.764 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@44ebcd03 testClass = HelloWorldControllerTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@694abbdc testClass = HelloWorldControllerTest, locations = '{}', classes = '{class helloworld.HelloWorldApp}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@2e005c4b key = [org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@80503, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@86be70a, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2c039ac6, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@e7e8512, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@25af5db5], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true]], class annotated with @DirtiesContext [false] with mode [null].
21:34:42.775 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [helloworld.HelloWorldControllerTest]
21:34:42.775 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [helloworld.HelloWorldControllerTest]
21:34:42.821 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=-1}

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.4.RELEASE)

2021-08-06 21:34:43.424  INFO 1788 --- [           main] helloworld.HelloWorldControllerTest      : Starting HelloWorldControllerTest on ip-172-31-21-123.ap-south-1.compute.internal with PID 1788 (started by ec2-user in /home/ec2-user/spring)
2021-08-06 21:34:43.427  INFO 1788 --- [           main] helloworld.HelloWorldControllerTest      : No active profile set, falling back to default profiles: default
2021-08-06 21:34:46.976  INFO 1788 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-08-06 21:34:47.606  INFO 1788 --- [           main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2021-08-06 21:34:47.606  INFO 1788 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Initializing Servlet ''
2021-08-06 21:34:47.627  INFO 1788 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Completed initialization in 21 ms
2021-08-06 21:34:47.678  INFO 1788 --- [           main] helloworld.HelloWorldControllerTest      : Started HelloWorldControllerTest in 4.843 seconds (JVM running for 6.724)
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 6.433 s - in helloworld.HelloWorldControllerTest
2021-08-06 21:34:48.180  INFO 1788 --- [       Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- maven-jar-plugin:3.1.1:jar (default-jar) @ spring-helloworld ---
[INFO] 
[INFO] --- spring-boot-maven-plugin:2.1.4.RELEASE:repackage (repackage) @ spring-helloworld ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.065 s
[INFO] Finished at: 2021-08-06T21:34:49Z
[INFO] Final Memory: 22M/52M
[INFO] ------------------------------------------------------------------------

The jar file spring-helloworld-0.1.0.jar is created. The following command is used for executing the jar file.

Java Command

java -jar target/spring-helloworld-0.1.0.jar

The output of the Java command is shown as below:

aws microservices - Hello World Beans
Spring Hello World Beans

2.6 Unit Tests

2.6.1 Spring

In Spring, MockMvc is used to send HTTP requests to the DispatcherServlet. The assertions are made based on the result obtained from the servlet. @AutoConfigureMockMvc annotation is used with @SpringBootTest to inject a MockMvc instance. The implementation of the Spring Unit Test is shown as below:

HelloWorldControllerTest

package helloworld;
 
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloWorldControllerTest {
 
    @Autowired
    private MockMvc mvc;
 
    @Test
    public void getMessage() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("Hello World\n")));
    }
}

Maven command is used to run the unit test. The command is as below :Maven Build Command

Maven Build Command

mvn package

The output of the executed command is shown below.

Output

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building spring-boot-demo 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ spring-boot-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ spring-boot-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ spring-boot-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/ec2-user/spring-boot-demo/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ spring-boot-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ spring-boot-demo ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.jcg.example.springbootdemo.SpringBootDemoApplicationTests
19:11:39.860 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
19:11:39.887 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
19:11:39.964 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.jcg.example.springbootdemo.SpringBootDemoApplicationTests] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
19:11:39.990 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.jcg.example.springbootdemo.SpringBootDemoApplicationTests], using SpringBootContextLoader
19:11:39.995 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.jcg.example.springbootdemo.SpringBootDemoApplicationTests]: class path resource [com/jcg/example/springbootdemo/SpringBootDemoApplicationTests-context.xml] does not exist
19:11:39.995 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.jcg.example.springbootdemo.SpringBootDemoApplicationTests]: class path resource [com/jcg/example/springbootdemo/SpringBootDemoApplicationTestsContext.groovy] does not exist
19:11:39.996 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.jcg.example.springbootdemo.SpringBootDemoApplicationTests]: no resource found for suffixes {-context.xml, Context.groovy}.
19:11:39.997 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.jcg.example.springbootdemo.SpringBootDemoApplicationTests]: SpringBootDemoApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
19:11:40.098 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.jcg.example.springbootdemo.SpringBootDemoApplicationTests]
19:11:40.217 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [/home/ec2-user/spring-boot-demo/target/classes/com/jcg/example/springbootdemo/SpringBootDemoApplication.class]
19:11:40.224 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.jcg.example.springbootdemo.SpringBootDemoApplication for test class com.jcg.example.springbootdemo.SpringBootDemoApplicationTests
19:11:40.367 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [com.jcg.example.springbootdemo.SpringBootDemoApplicationTests]: using defaults.
19:11:40.379 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
19:11:40.403 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@2ea41516, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@3a44431a, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@3c7f66c4, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@194bcebf, org.springframework.test.context.support.DirtiesContextTestExecutionListener@17497425, org.springframework.test.context.transaction.TransactionalTestExecutionListener@f0da945, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@4803b726, org.springframework.test.context.event.EventPublishingTestExecutionListener@ffaa6af, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@53ce1329, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@316bcf94, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@6404f418, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@3e11f9e9, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@1de5f259]
19:11:40.410 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@67f639d3 testClass = SpringBootDemoApplicationTests, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@6253c26 testClass = SpringBootDemoApplicationTests, locations = '{}', classes = '{class com.jcg.example.springbootdemo.SpringBootDemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@3c22fc4c, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@1184ab05, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@49d904ec, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@64cd705f], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true]], class annotated with @DirtiesContext [false] with mode [null].
19:11:40.469 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=-1}

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.2.RELEASE)

2021-08-07 19:11:40.845  INFO 8433 --- [           main] c.j.e.s.SpringBootDemoApplicationTests   : Starting SpringBootDemoApplicationTests on ip-172-31-21-123.ap-south-1.compute.internal with PID 8433 (started by ec2-user in /home/ec2-user/spring-boot-demo)
2021-08-07 19:11:40.851  INFO 8433 --- [           main] c.j.e.s.SpringBootDemoApplicationTests   : No active profile set, falling back to default profiles: default
2021-08-07 19:11:42.789  INFO 8433 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-08-07 19:11:42.827  INFO 8433 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 21ms. Found 0 JPA repository interfaces.
2021-08-07 19:11:43.813  INFO 8433 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-08-07 19:11:44.412  INFO 8433 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-08-07 19:11:44.864  INFO 8433 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-08-07 19:11:45.029  INFO 8433 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-08-07 19:11:45.186  INFO 8433 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {5.4.9.Final}
2021-08-07 19:11:45.506  INFO 8433 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2021-08-07 19:11:45.789  INFO 8433 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2021-08-07 19:11:46.700  INFO 8433 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-08-07 19:11:46.715  INFO 8433 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-08-07 19:11:47.467  WARN 8433 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2021-08-07 19:11:48.622  INFO 8433 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-08-07 19:11:49.999  INFO 8433 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 13 endpoint(s) beneath base path '/actuator'
2021-08-07 19:11:50.154  INFO 8433 --- [           main] c.j.e.s.SpringBootDemoApplicationTests   : Started SpringBootDemoApplicationTests in 9.671 seconds (JVM running for 11.509)
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.714 s - in com.jcg.example.springbootdemo.SpringBootDemoApplicationTests
2021-08-07 19:11:50.549  INFO 8433 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2021-08-07 19:11:50.554  INFO 8433 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-08-07 19:11:50.556  INFO 8433 --- [extShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
2021-08-07 19:11:50.568  INFO 8433 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2021-08-07 19:11:50.577  INFO 8433 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16.260 s
[INFO] Finished at: 2021-08-07T19:11:50Z
[INFO] Final Memory: 22M/53M
[INFO] ------------------------------------------------------------------------

2.7 What are Microservices?

Microservices are based on the single responsibility principle. The principle is related to grouping things that are impacted by the change. Single responsibility is related to SOLID principles. Robert C.Martin created this principle which states a single unit will have only one responsibility. Microservices architecture helps in creating applications that can be created and managed as different components or services. The components or services are loosely coupled and deployed separately. Every service performs a different type of work and interacts with others using API. Microservices help in creating resilience in the architecture. These services have routing like traditional Unix pipes thorough endpoints for information flow. Spring Boot has features to create and deploy microservices on the cloud and on the enterprise premises.

For example, an eCommerce application can have web and mobile user interfaces talking to different microservices such as user login, user authorization, product catalog, order management, shopping cart, payment, and delivery. Docker is used as a container for each of the services. Docker-compose helps in the orchestration of the services which are containerized.

Microservices are built using Spring Boot, Spark, Jersey, Restlet, Restx, Swagger, and Dropwizard frameworks. Microservices are based on design principles such as Domain Driven Design, Failure Isolation, Continuous delivery, Decentralization, and DevOps. Each microservice can be independently deployed, updated, replaced, and scaled. (DURS)

2.8 Benefits of Microservices

Development is faster in the case of microservices as the units of code required for a service is lesser. Deployment can be done independently and scaling the application is easier in the Microservices case. Fault Isolation is better in the case of Microservices architecture. Fixes for the error can be deployed on a specific service without impacting other services. The choice of the technology stack is very easy as each service can be built using a different programming language for a specific requirement. Each service can be integrated into different types of data sources such as relational database, no SQL database, hierarchical database, and web services.

Microservices apps will have lesser merges and code conflicts. Each microservice will have its own repository. Communication across distributed services can be challenging if one of the services is down.Security can be another challenge across multiple services. Each technology stack requires a separate pipeline or workflow for deployment. The readability of the code is affected as the services are decoupled. Configuration of multiple microservices needs to be maintained which can be a challenge (for 100 microservices). DevOps and automation is the only way to deploy and manage these microservices. Centralized logging need to be there to isolate the issue specific to a microservice.

2.9 Why AWS? 

AWS is better than any other cloud vendor because of the following reasons:

  • It is easy for developers to use AWS
  • AWS has different tools and Services for hosting apps
  • It has elasticity capability to manage capacity
  • It is reliable, secure, and manageable.
  • It is available in different geographies

2.10 Microservices on AWS

You can deploy the spring boot demo code on AWS ec2 first instance as shown below:

Java Setup

ssh -i aws_ec2.pem ec2-user@ec2-13-233-212-76.ap-south-1.compute.amazonaws.com
sudo yum install java-1.8.0-openjdk-devel
java -version
alternatives --config java
export JAVA_HOME="/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.302.b08-0.amzn2.0.1.x86_64"

 PATH=$JAVA_HOME/bin:$PATH
sudo wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
 sudo sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo
 sudo yum install -y apache-maven
 mvn --version

you can execute the spring boot demo folder code by using the command below:

Maven Build Command

mvn package
java -jar target/spring-helloworld-0.1.0.jar

Before accessing the deployed spring microservice on AWS, you need to modify the security group.

aws microservices - inbound rules
AWS Inbound Rules

You can access the application at :

AWS Microservice

You can change the port as 80 using the command below:

Port 80 setup

 sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080

2.11 Implementing microservices on AWS

You can create an account on AWS and create anothe instance using AWS Linux AMI template. You can download the .pem file while inputing the key pair. You can save the .pem file as aws_ec2.pem. You can use the command below to ssh into aws console. You can use ec2-user account to login into the instance created. (ec2-13-233-212-76.ap-south-1.compute.amazonaws.com)

Java Setup

ssh -i aws_ec2.pem ec2-user@ec2-13-233-212-76.ap-south-1.compute.amazonaws.com
sudo yum install java-1.8.0-openjdk-devel
java -version
alternatives --config java
export JAVA_HOME="/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.302.b08-0.amzn2.0.1.x86_64"

 PATH=$JAVA_HOME/bin:$PATH
sudo wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
 sudo sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo
 sudo yum install -y apache-maven
 mvn --version

you can execute the spring boot demo 2 folder code by using the command below:

Maven Build Command

mvn package
java -jar target/spring-helloworld-0.1.0.jar

You can now create a classic load balancer as shown below.

Classic Load Balancer create

You can add both the instances which are running the same microservice as shown below:

Classic Load Balancer – Add Instances

Initially, the instances will show out of service. After 2 to 3 minutes, they will be in service as shown below:

In Service – 2 EC2 insances

You can access them through the loadbalancer (http://awsclb-227035050.ap-south-1.elb.amazonaws.com/hello/) as shown below:

AWS Instance 1 – Microservice

You can access the instance 2 by trying the same url – 4 to 5 times.

AWS Instance 2 – Microservice

3. Download the Source Code

Download
You can download the full source code of this example here: Implementing Microservices on AWS

Bhagvan Kommadi

Bhagvan Kommadi is the Founder of Architect Corner & has around 20 years’ experience in the industry, ranging from large scale enterprise development to helping incubate software product start-ups. He has done Masters in Industrial Systems Engineering at Georgia Institute of Technology (1997) and Bachelors in Aerospace Engineering from Indian Institute of Technology, Madras (1993). He is member of IFX forum,Oracle JCP and participant in Java Community Process. He founded Quantica Computacao, the first quantum computing startup in India. Markets and Markets have positioned Quantica Computacao in ‘Emerging Companies’ section of Quantum Computing quadrants. Bhagvan has engineered and developed simulators and tools in the area of quantum technology using IBM Q, Microsoft Q# and Google QScript. He has reviewed the Manning book titled : "Machine Learning with TensorFlow”. He is also the author of Packt Publishing book - "Hands-On Data Structures and Algorithms with Go".He is member of IFX forum,Oracle JCP and participant in Java Community Process. He is member of the MIT Technology Review Global Panel.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button