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.
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:
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.
You can access the application at :
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.
You can add both the instances which are running the same microservice as shown below:
Initially, the instances will show out of service. After 2 to 3 minutes, they will be in service as shown below:
You can access them through the loadbalancer (http://awsclb-227035050.ap-south-1.elb.amazonaws.com/hello/) as shown below:
You can access the instance 2 by trying the same url – 4 to 5 times.
3. Download the Source Code
You can download the full source code of this example here: Implementing Microservices on AWS