“The patterns for how we develop software, both in teams and as individuals, are rapidly evolving.”
This article is a deep-dive into Josh Long‘s talk on Cloud Native Java, ahead of Voxxed Days CERN. In this article, we’re going to look at what a cloud native system is, what a ‘good’ distributed system is, and finally, start to build a basic example based on the Cloud Native Workshop.

Philosophy

In order to delivery software at an increasingly fast pace, we need to think carefully about the behaviour of an application in production, rather than an application as a product of an architecture diagram.

diagram

On paper

server on fire

In reality

Microservices for faster features

We  benefit from looking at using Conway’s Law to our advantage. Conway’s Law is the observation that “Any organisation that designs a system … will inevitably produce a design whose structure is a copy of the organisation’s communication structure.” So – what if we design a system that facilitates a transformation of culture: that mirrors how you want the organisation to communicate. One of these is microservices: allowing teams to quickly, independently develop, test and deploy focused services.

Distributed systems bring complexity

One of the problems with microservices is that you get a distributed system, which brings complexity. They also bring operational challenges. The way the services communicate has to take into account the fact that one service might be in one cloud, while a downstream service is in another. You have to adapt to a different way of managing services without a fixed ‘place’.

We need to reduce or abstract away the complexity

So for a product that needs speed, we can split it into features that can be independently developed and deployed, and take advantage of the cloud in order to scale horizontally. Once everything is split out, we need to work out how to manage it. Cloud Native is “a style of application development that encourages easy adoption of best practices in the areas of continuous delivery and value-driven development.”

A good starting point it to look at the qualities of a ‘good’ distributed system.

A good distributed system

  1. Is agile – is easy to evolve. There isn’t one way to develop software, or run it, and new technologies are springing up constantly. A system should be fast and easy to change.
  2. Takes advantage of the elasticity of a cloud environment
  3. Failures do happen. A good system should do the right thing in the face of failure.
  4. It has to be observable, both in terms of individual services and as a whole.

We want to move towards abstractions that facilitate the 4 tenants in Java.

Cloud Native Java in action

In order to demonstrate how it is possible to start building a ‘good’ distributed system, we’re going to look at the basics of the Cloud Native Workshop using Spring:

“What is Spring? Spring, fundamentally, is a dependency injection container. This detail is unimportant. What is important is that once Spring is aware of all the objects – beans – in an application, it can provide services to them to support different use cases like persistence, web services, web applications, messaging and integration, etc.”

At the end of this deep-dive, you should be ready to follow the talk in real time, and have a good feel for the code examples. You will need JDK 8, Maven, and an IDE.

Service

Go to http://start.spring.io/. Here, we can quickly generate a new Maven Project with Spring Boot. Spring Boot aims to provide a quick way to get started with building production ready cloud native applications.

We’re going to chose:

  • H2: an in memory database
  • Web: for full stack development with Tomcat and Spring MVC
  • Config Client: this will allow us to connect to a configuration server that we will create later
  • Rest Repositories: so we can make RESTful calls
  • JPA: Java Persistence API
  • Actuator: Production ready features for monitoring and managing

Spring Initializr

We get a zip file: reservation-service.zip.

ReservationServiceApplication

In reservation-service, we can add a JPA entity type – reservation:

/**
 * Build a JPA entitiy type - Reservation.
 * Manage with a primary key
 */
@Entity
class Reservation {

	@Id
	@GeneratedValue
	private Long id;

	private String reservationName; // reservation_name

	@Override
	public String toString() {
		return "Reservation{" +
				"id=" + id +
				", reservationName='" + reservationName + ''' +
				"}";
	}
	
	//Constructor
	public Reservation(String name){
		this.reservationName = name;
	}

	//JPA requires a default constructor.
	public Reservation() {}
	
	//Getters and setters
	public Long getId() {
		return id;
	}
	public String getReservationName() {
		return reservationName;
	}
	public void setId(Long idToSet) {
		this.id = idToSet;
	}
	public void setReservationName(String reservationName){
		this.reservationName = reservationName;
	}
}

We can add an interface to access the JPA repository:

/*
 * Want to be able to read, write and work with instances of this type
 * And we want it to be a REST API, so we have spring-boot-starter-data-rest
 */
@RepositoryRestResource
interface ReservationRepository extends JpaRepository<Reservation, Long> {

}

And finally we can initialise some data:

/*
 * Now we want some sample data - CLR is a callback interface in Spring Boot
 * It allows us to do application initialisation
 */
@Component
class SampleDataCLR implements CommandLineRunner {

	private final ReservationRepository reservationRepository;

	//Going to inject some data here
	@Autowired
	public SampleDataCLR(ReservationRepository reservationRepository) {
		this.reservationRepository = reservationRepository;
	}

	@Override
	public void run(String... strings) throws Exception {
		Stream.of("Josh","Juergen","Andrew","Bridget","Onsi","Phil").forEach(name -> reservationRepository.save(new Reservation(name)));
		//Check this has worked
		reservationRepository.findAll().forEach(System.out::println);
	}
}

Now if we run this, and go to http://localhost:8080/reservations, we can see the data:

reservation data

Spring Boot Actuator allows us to navigate to metrics (to see available information about the application, like threads, heap size etc) and heath to check the system status.

The Twelve-Factor App

There is a methodology, the Twelve-Factor App, that provides guidelines for building software-as-a-service apps. The third factor is that configuration should be separate from the code.

“A litmus test for whether an app has all config correctly factored out of the code is whether the codebase could be made open source at any moment, without compromising any credentials.”

In order to meet this condition, and ensure that configuration can be changed without having to restart applications, or duplicate it across services, we can build a configuration server. When we have a distributed system, a configuration server allows us to:

  • Create an API that manages a directory of configuration.
  • Track changes to the configuration (by using a directory based on Git, for example).
  • Encrypt configuration like passwords, locators etc.
  • Keep the configuration in a centralised place, and change it once at source.
  • Change configuration live without restarting applications.

Config server

To quickly get started, clone or download this repository into a folder called ‘config’. This contains a set of configuration property files for the microservices (not all of these will be used in this deep-dive). Then we need to go back to the Spring Initializr and create a config-server:

config-server

Opening up this project, we need to enable it as a configuration server by going to ConfigServerApplication.java:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConfigServerApplication.class, args);
	}
}

In src/main/resources, we can go into application.properties and point it at the config folder we just created, and at port 8888:

spring.cloud.config.server.git.uri = file://${HOME}/Documents/Programming/config 
server.port = 8888

N.B. you may need to initialise an upstream git repository for the config folder.

Now if you navigate to http://localhost:8888/reservation-service/default, you will see the default configuration. Note the “Hello World!” message.

Config defaults

We can change this in reservation-service.properties in the config folder:

message = Hello CERN!

and immediately see this updated at http://localhost:8888/reservation-service/default.

In order for the reservation-service to be aware of the configuration, we use the dependency spring-cloud-starter-config that you can see in the reservation-service pom.xml. In reservation-service application.properties, we add:

spring.cloud.config.uri = http://localhost:8888
spring.application.name = reservation-service

Now, restarting the reservation-service, you should see the logging:

c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: http://localhost:8888

The reservation-service is now at port 8000, as specified in the config.

Going further…

It is possible to go further and make the configuration dynamic, for example by using the Spring Cloud @RefreshScope bean. This allows you to refresh a bean either in the code using a public method refreshAll(), or using the exposed /refresh endpoint. This clears the target cache. You can then recreate a bean without having to recreate the service.

With dynamic configuration, comes access to feature-flags for quickly turning on or off features, A/B testing, et cetera.

The next logical step is to manage mapping between services, and allow them to talk in a safe and consistent way. We can do this by using a service registry. This could provide a logical mapping from service IDs to hosts and ports. One way to do this is by using Netflix Eureka, which is Spring Cloud’s discovery service abstraction.

Service Registry

We need to add the Eureka dependency to the reservation-service pom.xml:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

And then add the bean @EnableDiscoveryClient in ReservationServiceApplication.java above @SpringBootApplication:

@EnableDiscoveryClient 
@SpringBootApplication 
public class ReservationServiceApplication { 
    public static void main(String[] args) { 
        SpringApplication.run(ReservationServiceApplication.class, args); 
    }
}

Back at http://start.spring.io/ we can generate the eureka-service with the Eureka Server and Config Client:
eureka service
In application.properties, we want to point the service at the configuration server:

spring.application.name = eureka-service
spring.cloud.config.uri = http://localhost:8888

And add the @EnableEurekaServer bean to EurekaServiceApplication.java

@EnableEurekaServer
@SpringBootApplication
public class EurekaServiceApplication {

	public static void main(String[] args) {
		SpringApplication.run(EurekaServiceApplication.class, args);
	}
}

We now need to start the eureka-service. Restarting the reservation-service, it will call the registry and register itself as available to that service. At http://localhost:8761/, we can see the registered services.eureka at work

Now if we want to create a number of upstream clients, they can be agnostic of the location of the downstream services. They can easily be spun up per concern.

What next?

We have the beginnings of an easy-to-evolve cluster of microservices, that take advantage of the elasticity of the cloud environment. It is easy to spin up new services and takes barely any code to facilitate this: the framework does the hard work. We can add circuit breakers to handle failure, and services to check the ‘health’ of the system.

There are vast possibilities: hopefully you have the background experience to get the most out of Cloud Native Java. Josh will be giving the talk at Voxxed Days CERN on the 25th Feb 2017, and Voxxed Days Singapore on the 2nd June 2017.

Voxxed Days CERN

Deep-dive: Cloud Native Java

About The Author
- Katharine is a Java developer by trade, turned Community & Content Manager for Voxxed. Helping developers learn and share knowledge. Contact me at kbe@voxxed.com with any news, articles or tutorials.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>