Spring Microservices with MongoDb

Distributed applications is always a current topic in the articles and forum discussions; Microservices answers the need for application scalability and makes a good marriage with Cloud solution (AWS) or Virtual container (Docker).

In this post I’d like to do a little introduction of the concept behind these solution type.

As said above, the distributed applications are not totally a new idea. J2EE (now called JEE), SOA architecture and even the old DCom solution from Microsoft have tried to split a single application into multi modules in order to increase or decrease the number of resources for a single layer (FrontEnd, BackEnd, Dao layer, etc…) and achieve scalability result.

So, what’s news is worth to be said?

Microservices come with the same concept but smarter than the previous solutions. SOA used the very verbose Web Services and JEE has a very weighty infrastructure. As the word “micro” suggests, this technology comes more easy to get, to apply, to develop and deploy.

I’d recommend to read Eric Knorr’s article here about Microservice and their predecessors parents (http://www.javaworld.com/article/2683277/architecture-scalability/what-microservices-architecture-really-means.html).

As usual, I’d use an example to introduce this technology.

Starting from this example (https://github.com/cer/microservices-examples), I’ve built an example using two layers (Business Logic and FrontEnd) while the data is stored using MongoDb.

micro1

Pay attention, we’re not talking at the MVC (model, view, controller) model that we’re accustomed to view.

The main concept here it’s to look the first two blocks independent from each other and without knowledge of their existence.

One of the server/service who makes this available is Eureka Server from Netflix. More information here (https://github.com/Netflix/eureka/wiki).

So, every service has registered on Eureka and, in this way, every service gets the knowledge of the services registered on Eureka Server.

micro2

The next step of this schema is easy to make, specially when you think of scalability system.

micro3

You can add every single node and register it on Eureka Server to let the other servers able to communicate with them.

The project configuration files have the duty to describe the above schema. The first is relative at the Eureka server. I’ve assumed to configure a cluster of Eureka Server and have put peer1 and peer2 as localhost in hosts file.


# Configure this Discovery Server
eureka.instance.hostname=peer1
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false

# Configure the cluster nodes 
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/ 

# HTTP (Tomcat) 
port server.port=1111

# Application Name 
spring.application.name=PEER1 

And the cluster 2


# Configure this Discovery Server
eureka.instance.hostname=peer2
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false

# Configure the cluster nodes 
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/ 

# HTTP (Tomcat) 
port server.port=1112

# Application Name 
spring.application.name=PEER2

The service named Business Logic is led by this file.


#Eureka Service
eureka.instance.leaseRenewalIntervalInSeconds=5
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer1:1112/eureka/

#MongoDb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=Booking

#Application Name
spring.application.name=BOOKING-SERVICE

# HTTP Server
server.port=9000

I’ve configured the MongoDb server and the Eureka cluster.

The service name Web is defined by this file.


#Eureka Service
eureka.instance.leaseRenewalIntervalInSeconds=5
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer1:1112/eureka/

#Spring view resolver
spring.mvc.view.prefix=/WEB-INF/
spring.mvc.view.suffix=.jsp

#Application Name
spring.application.name=WEBMVC-SERVICE

# HTTP Server
server.port=8080

As for Business Logic service, the Eureka cluster is defined and Spring view resolver configured.
I used JSF to build the user interface instead of ThymeLeaf.

Now, the Java code. This is quite similar to Chris Richardson code in the github example.

Eureka server starts as SpringBoot application and sets the configuration file as eureka-server.properties


@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {

public static void main(String[] args) {
System.setProperty("spring.config.name", "eureka-server");

SpringApplication.run(EurekaServer.class, args);
}
}

Quite similar the code for the cluster node 2.


@SpringBootApplication
@EnableEurekaServer
public class EurekaServerNode2 {

public static void main(String[] args) {
System.setProperty("spring.config.name", "eureka-server-node2");

SpringApplication.run(EurekaServerNode2.class, args);
}
}

More interesting is the Business Logic layer.


@SpringBootApplication
@EnableAutoConfiguration
@EnableDiscoveryClient
public class BookingServer {

public static void main(String[] args) {
System.setProperty("spring.config.name", "booking-server");
SpringApplication.run(BookingServer.class, args);
}
}

The attribute @EnableDiscoveryClient actives the discovering of the Eureka Server by the service.

The Business Logic layer is a rest service which store the data into MongoDb.


@RestController
public class BookingController {

@Autowired
private BookingRepository repository;

@RequestMapping("/list")
public List<Booking> getColleagues(){
return repository.findAll();
}

/*
*  POST http://localhost:8080/save HTTP/1.1
User-Agent: Fiddler
Content-Type: application/x-www-form-urlencoded

code=JKS22&flightNumber=AZ-231&name=John&surname=Smith&seat=1A
*/
@RequestMapping(method = RequestMethod.POST, path="/add")
public ResponseEntity<String> addColleague(@ModelAttribute Booking booking){

repository.save(booking);

return new ResponseEntity<>(HttpStatus.CREATED);
}

}

The Web layer that calls the Business Logic layer is configured as:


@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan
public class WebServer extends SpringBootServletInitializer{

public static void main(String[] args) {

System.setProperty("spring.config.name", "webmvc-server");
/*
* Disable thymeleaf
*/
System.setProperty("spring.thymeleaf.enabled", "false");
SpringApplication.run(WebServer.class, args);
}

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(WebServer.class);
}

@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}

}

The RestTemplate is used to call the Business Logic layer with the service registration name Booking-Service.


@Controller
public class WebController {

@Autowired
@LoadBalanced
protected RestTemplate restTemplate;

@RequestMapping("/")
public ModelAndView welcome() {
return new ModelAndView("jsp/home", "command", new Booking());
}

@RequestMapping("/bookings")
public ModelAndView getBooking() {

Booking[] booking = restTemplate.getForObject("http://BOOKING-SERVICE/list", Booking[].class);

ModelAndView model = new ModelAndView("jsp/list");
model.addObject("bookings", Arrays.asList(booking));
return model;

}

@RequestMapping(path="/add", method=RequestMethod.POST)
public ModelAndView addBooking(@ModelAttribute Booking booking) {

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, Object> variables = buildRequest(booking);

HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(variables, requestHeaders);
ResponseEntity<?> response = 
restTemplate.postForEntity("http://BOOKING-SERVICE/add", requestEntity, ResponseEntity.class);

System.out.println(response);

return welcome();

}

protected MultiValueMap<String, Object> buildRequest(Booking booking)
{
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
param.add("code", booking.getCode());
param.add("FlightNumber", booking.getFlightNumber());
param.add("Name", booking.getName());
param.add("Surname", booking.getSurname());
param.add("Seat", booking.getSeat());
return param;
}
}

Let’s get to run the solution!

The two nodes of Eureka servers.


java.exe -classpath SpringMicroServiceBooking.jar;lib\*; it.blog.microservice.eureka.EurekaServer

...

2017-01-27 16:16:48.146  INFO 2216 --- [main] i.blog.microservice.eureka.EurekaServer : 
Started EurekaServer in 24.587 seconds (JVM running for 29.549)

The nodo 2


java.exe -classpath SpringMicroServiceBooking.jar;lib\*; it.blog.microservice.eureka.EurekaServerNode2

...

2017-01-27 16:17:04.501  INFO 8588 --- [main] i.b.m.eureka.EurekaServerNode2 : 
Started EurekaServerNode2 in 26.212 seconds (JVM running for 27.441)

The Business Logic

java.exe -classpath SpringMicroServiceBooking.jar;lib\*; it.blog.microservice.booking.BookingServer

And, finally, the Web layer.


java.exe -classpath SpringMicroServiceBooking.jar;lib\*; it.blog.microservice.webmvc.WebServer

Don’t forget to run the MongoDb instance; if you don’t want to use a separate one, you can embedded a MongoDb instance inside the Booking Layer. More information here.
Now, browsing http://peer1:1111/ to access at the Eureka dashboard.

micro_dashboard

You can notice the cluster node 2 (peer2) and the application registered (Booking and WebMvc Service).

The WebMvc-Service communicates with the other layer through a Rest service. Notice that I used the Service address BOOKING-SERVICE instead of  the Tomcat address and port (9000).

That’s possible thanks to the @LoadBalanced annotation of the RestTemplate object.

Let’s start the dashboard at localhost:8080

dashboard1

Once added a new customer you can look at the log for the Booking layer and check the new one in the Booking list.

Stopping one Eureka note you shouldn’t receive any problem adding a new record. Eureka registered applications are replicated to each node and, the fail of one, don’t due any network error.

The complete code of the solution is available on GitHub (https://github.com/MarcoGhise/SpringMicroServiceBooking)

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s