Basics for Setting Up a Microservices Architecture in a Project for Spring Boot and Gradle
Learn about the components needed to build microservices architecture in a project for Spring Boot and Gradle to enable continuous delivery/deployment.
Join the DZone community and get the full member experience.
Join For FreeMicroservices, also known as the microservices architecture, is an architectural style that structures an application as a collection of loosely-coupled services, which implement business capabilities. Microservice architecture enables the continuous delivery/deployment of large, complex applications, and allows organizations to evolve their technology stack. Its main advantage is scaling along with deployments. Below you'll find the essentials for a simple web application built using microservices architecture.
1. Spring Boot
Spring Boot makes it easy to create stand-alone applications with tomcat installed, which you can run by starting the jar file. A Spring Boot app does not require any kind of XML configurations; everything is done using just annotations. It is very simple to create a web app using Spring Boot. Below, you can see an example of a Spring Boot controller, which makes it so simple to create a web app with a REST service:
@Controller
@EnableAutoConfiguration
public class SampleController {
@RequestMapping("/")
@ResponseBody
String home() {
return "Hello World!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleController.class, args);
}
}
2. Gradle
Gradle is a Java building tool similar to Maven and Ant. Gradle is more powerful than both, as it is a combination of Maven and Ant. Gradle does not require any XML file, as it has its own DSL based in Groovy. Gradle is much simple and cleaner than Maven or Ant. We have the build.gradle file in it, which includes all the dependencies required for a web app. It also includes the jar name to be generated along with Java, Hibernate, and Database versions. Below is a code snippet from the build.gradle file:
apply plugin: 'java'
apply plugin: 'checkstyle'
apply plugin: 'findbugs'
apply plugin: 'pmd'
version = '1.0'
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3'
}
3. Discovery Server
Discovery Server is mainly used to have all the microservices' clients connected at a central place so that they can easily communicate. Eureka Discovery receives heartbeat messages from each instance belonging to a service. If the heartbeat fails over a configurable timetable, the instance is normally removed from the registry. By having @EnableDiscoveryClient
, you can easily create a discovery client in a Spring Boot app.
@SpringBootApplication
@EnableEurekaServer
@EnableDiscoveryClient
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
For all the discovery clients, you need to add the configuration below into the application.yml of each client module to locate its discovery server:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
4. Central Config Server
The main functionality of having central-config-server is for storing all kind of configuration properties at a central place so that we do not need to go to each core module explicitly to change properties. It is actually connected to the discovery server, which makes it easy for each core's microservices to get its properties files. Whenever a change is made to a property file, we can just restart this server and the core module whose property file is changed; you will not even require any kind of build for the core module to get the updated properties. You place properties files at any specific location (Git, etc) and just specify the path of properties in the application.yml file. The code snippet below will give you an overview of the Central Config server:
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class CentralConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(CentralConfigServerApplication.class, args);
}
}
application.yml
spring:
profiles:
active: native
cloud:
config:
server:
native:
searchLocations: file:./properties,classpath:config/
5. Gateway Server
Gateway/Zuul is an edge service that provides dynamic routing, monitoring, resiliency, security, and more. The main purpose of this is to provide security and routing for the core microservices. We can have different types of filters in the Gateway server so that we can manage security for any type of API call to the core microservices. It acts as a proxy between core microservices and the outside applications.
package com.example.EmployeeZuulService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class EmployeeZuulServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EmployeeZuulServiceApplication.class, args);
}
}
application.yml
zuul:
prefix: /application_name
ignoredServices: "*"
routes:
coreservice_name: /coreservice_name/**
6. Orchestra Microservices Layer
The use of this layer in microservice architecture is to combine different kinds of responses from multiple core services and do more processing on the data, then publish them in the response. The main need for this layer is less as compared to all other layers. It is just a Spring Boot app which is communicating to discovery, gateway, and microservices but does not have any kind of interactions with the database part.
@RestController
@RequestMapping("orchestra")
public class OrchestraController {
@Autowired
@LoadBalanced
protected RestTemplate restTemplate;
protected Logger LOGGER = LoggerFactory.getLogger(getClass());
@ApiOperation(value = "Retrieve combined list from two microservices")
@RequestMapping(value="/combinedlists", method=RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Object>> getCombinedList(HttpServletRequest request) {
LOGGER.info("Inside getCombinedList method");
List<Object> combinedList = new ArrayList<>();
try {
String url1 = "http://"+"<core_microservice_name1>"+"rest_api_url1";
String url2 = "http://"+"<core_microservice_name2>"+"rest_api_url2";
ResponseEntity<List<Object>> result1 = restTemplate.exchange(url1, HttpMethod.GET, null,new ParameterizedTypeReference<List<Object>>() {});
ResponseEntity<List<Object>> result2 = restTemplate.exchange(url2, HttpMethod.GET, null,new ParameterizedTypeReference<List<Object>>() {});
List<Object> list1 = result1.getBody();
List<Object> list2 = result2.getBody();
combinedList.addAll(list1);
combinedList.addAll(list2);
}
catch(Exception e) {
LOGGER.error("Exception in getCombinedList method:",e.getMessage());
return new ResponseEntity<List<Object>>(combinedList,HttpStatus.INTERNAL_SERVER_ERROR);
}
LOGGER.info("Exit from getCombinedList method");
return new ResponseEntity<List<Object>>(combinedList,HttpStatus.OK);
}
}
7. Core Microservices Layer
This is the lowest layer in the microservices architecture, which actually performs a lot of operations on the database and process the data as per the need. The actual REST services are written in the core layer. This part does each operation of different transactions.
It has connections with the discovery through the @EnableDiscoveryClient
annotations. As we already added environment level configurations in the central config server, we can still have application-level configuration settings/messages in the application.properties in the core module itself.
SampleServiceApplication.java
@SpringBootApplication
@EnableDiscoveryClient
public class SampleServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SampleServiceApplication.class, args);
}
}
SampleServiceController.java
@RestController
public class RecommendationService {
private static final Logger LOG = LoggerFactory.getLogger(RecommendationService.class);
@Autowired
private SetProcTimeBean setProcTimeBean;
@Autowired
private LoadBalancerClient loadBalancer;
private RestTemplate restTemplate = new RestTemplate();
@RequestMapping("/recommendation")
public List<Recommendation> getRecommendations(@RequestParam(value = "productId", required = true) int productId) {
List<Recommendation> list = new ArrayList<>();
list.add(new Recommendation(productId, 1, "Author 1", 1, "Content 1"));
list.add(new Recommendation(productId, 2, "Author 2", 2, "Content 2"));
list.add(new Recommendation(productId, 3, "Author 3", 3, "Content 3"));
return list;
}
}
application.yml
server:
port: 7878
eureka:
instance:
leaseRenewalIntervalInSeconds: 10
metadataMap:
instanceId: ${vcap.application.instance_id:${spring.application.name}:${spring.application.instance_id:${random.value}}}
client:
registryFetchIntervalSeconds: 5
bootstrap.yml
spring:
application:
name: sample
cloud:
config:
enabled: true
discovery:
enabled: true
serviceId: central-config-server
References
https://spring.io/guides/gs/centralized-configuration/ x
https://spring.io/guides/gs/service-registration-and-discovery/ x
Opinions expressed by DZone contributors are their own.
Comments