Learning Microservices with Spring and Spring boot
Installing Maven, Download Maven, extract the zip folder, update bin folder path in system path variable. Verify Maven installation with 'mvn -version' command in command prompt. Download and install STS (Spring Tool Suite).
- Can be created in many ways using
- Spring CLI (Command line interface)
- Spring Tool Suite IDE
- Spring Initializr (A web interface accessible from http:start.spring.io)
- To install spring cli, Download the cli zip from spring.io and add the bin folder in system path variables.
- Open STS >> File >> New >> Spring Starter Project
- Provide a name, Type as maven, packaging as jar, java version as 8, language as jar.
- You can edit the other details as per need >> Click next
- Select spring boot version you prefer.
- Select Spring web or web and click finish.
- The application will be created with Bill of material pattern.
-
We will have a single application class with annotation @SpringBootApplication
-
Which will say to spring that this is our primary component.
-
@SpringBootApplication encapsulates three other annotations
- @Configuration - Which specifies the contained class have one or more @Bean definitions
- @EnableAutoConfiguration - Will enable spring to automatically configure the spring application, based on the dependencies present in the class-path.
- @ComponentScan - @Configuration is meta annotated with @Component so they are eligible for component scanning.
-
Creating a new application class for the enpoints(url)
-
Aim is to display a message on invoking the url /greeting.
-
Create an object with string for sending message, which contains getters and setters
-
Create a class with an annotation '@RestController', This annotation will create a restful implementation of a service.
-
Programs for the above mentioned are in the project InitialApp
-
To run this application >> Click on project >> Run as >> Spring boot application
Command to run a spring boot application from command line
mvn spring-boot:run
- Open command prompt, Navigate to the project folder
- run
mvn install
>> After it completes you will find a jar in the target folder. - Here the jar generated is InitialApp-0.0.1-SNAPSHOT.jar
- To run this jar use
java -jar target/InitialApp-0.0.1-SNAPSHOT.jar
- Tools to test a rest application postman, soap-ui, paw, Advanced Rest Client.
- easiest way is to run
curl localhost:8080
in a command prompt.
We can also test the service by using the default test class generated by spring boot. The code for the same is present in the initial application app.
- Annotation that allows the testing of spring boot applications, by enabling spring boot features during test execution.
- webEnvironment = WebEnvironment.RANDOM_PORT property directs spring boot, to bind application to random port, which will be handy during a test involving multiple instances of same app.
- TestRestTemplate is being used for calling a restful service. This also abstracts the lower level details of HTTP client.
- TestRestTemplate automatically detects the actual port used by spring boot.
To test this open terminal and run
mvn install
From STS open Junit view and click on run test.
- Useful for building conversational style microservices which exhibit strong affinity between UI and its backend services.
- HATEOAS is a REST service pattern in which navigation links are provided as part of payload metadata.
- Useful methodology for responsive mobile and web applications, where client downloads additional data based on user navigation pattern.
- HAL is a format based on JSON, which establishes conventions for representing hyperlinks between resources.
- HAL Browser is a handy API browser for HAL+JSON data. Helps APIs to be more explorable and discoverable.
- Choose Spring Web, Spring HATEOAS and Spring HAL Browser. Generate the project.
- Example HATEOAS project is attached.
- To access HAL browser navigate to
/browser/index.html
URI. - for the attached project
http://localhost:9000/browser/index.html
- Combines reactive programming paradigm with Microservice architecture.
- Reactive software are
- Responsive : Responsive systems focus on providing rapid and consistent response times, establishing reliable upper bounds so they deliver a consistent quality of service.
- Resilient: The system stays responsive in the face of failure. This applies not only to highly-available, mission-critical systems โ any system that is not resilient will be unresponsive after a failure. Resilience is achieved by replication, containment, isolation and delegation.
- Elastic: The system stays responsive under varying workload. Reactive Systems can react to changes in the input rate by increasing or decreasing the resources allocated to service these inputs.
- Message Driven: Reactive Systems rely on asynchronous message-passing to establish a boundary between components that ensures loose coupling, isolation and location transparency.
- A highly reliable and scalable message system is the single most important component in a reactive microservice ecosystem.
- QBit, Spring Reactive, RxJava and RxJS are some of the frameworks and libraries to build reactive microservices.
- Example use case of reactive microservice: Order management system. On placing a an order an event will be triggered, Which will initiate series of services like check inventory, replenish stock, confirm payment, initiate packing, Initiate shipping.
- Webflux is used to implement reactive microservices in spring, It can be implemented in 2 ways
- using @Controller and other annotations in spring boot
- Functional programming using java8 lambda style.
- Create a new spring maven project by selecting only Web >> Spring reactive web >> Generate project
- Instead of spring starter
spring-boot-starter-webflux
will be available in POM. - Using the same controller and objects as used earlier.
- Instead of returning a list we will return a Construct Mono.
- This allows the spring for reactive programming
@GetMapping("/")
public Mono<Greet> displayMessage() {
return Mono.just(new Greet("Hi There"));
}
- In the above example the Greet object will be serialized only when Mono is completed in asynchronous non-blocking mode.
- Use of mono will create a single definitive item. Here Mono is used to declare a logic which will get executed as soon as the object is de-serialized.
- If in case Mono is sent as a parameter to a controller method, it may be executed even before the serialization gets over. The code in the controller will let us know what we need to do with Mono object. Alternative to Mono we can use Flex.
- We will use web-environment with defined port instead of random port.
- WebTestClient is used to create a server and bind with a base URL to it.
@Test
public void testWebFluxURI() throws Exception {
WebTestClient webClient = WebTestClient.bindToServer().baseUrl("http://localhost:8080").build();
webClient.get().uri("/")
.accept(org.springframework.http.MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody(Greet.class).returnResult()
.getResponseBody().getMessage().equalsIgnoreCase("Hi There");
}
- Reactive stream specification has implementations of four interfaces they are as follows
- A publisher holds the source of data.
- Publishes data at the request of subscriber.
- A subscriber can then attach a subscription on the publisher.
Subscribe method is just a registration method, will not return any result.
public Interface Publisher<T>{
public void subscribe(Subscriber<? super T> s);
}
- Subscriber subscribe to a publisher for consuming streams of data.
- It defines a set of call back methods which will be called up on those events.
OnComplete is when everything is done and successful. All the below mentioned methods are call back methods and they do not respond with any data
public interface Subscriber<T> {
public void onSubscribe(Subscription s);
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
- Subscription is shared by exactly one publisher and one subscriber for the purpose of data mediation between these pair.
Data exchange happens when the subscriber calls request method. To stop subscription cancel method is invoked.
public interface Subscription {
public void request(long n);
public void cancel();
}
- A processor represents a processing stage which is both a subscriber and publisher, It should obey the contracts of both.
- This can be chained by connecting a publisher and subscriber.
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
}
- Reactor has two implementations for publisher
- Flux: Can emit (0...n) events
- Mono: Can emit (0..1) event
Flux is required when many data elements or a list of values is transmitted as streams.
- Download rabbit MQ and install it as specified in the site: https://www.rabbitmq.com/download.html
- For installing rabbit MQ erlang is also required so install the same.
RabbitMQ runs on port 5672 by default, User name and password will be guest.
- To create a event driven messaging project choose Messaging >> Spring RabbitMQ >> generate project
- RabbitMQ need to be configured in Application properties as below
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
server.port=9090
- Now we need to define sender and receiver components
Complete code is available in attached SpringRabbitMQ project
- Sender should initiate a Queue, RabbitMQMessagingTemplate and send methods to send messages.
@Autowired
RabbitMessagingTemplate template;
@Bean
Queue queue() {
return new Queue("TestQ", false);
}
public void send(String message) {
template.convertAndSend("TestQ",message);
}
- Receiver is initiated to have a RabbitListener
@RabbitListener(queues = "TestQ")
public void processMessage(String content) {
System.out.println(content);
}
- To initiate this message queue we need to implement the CommandLineRunner interface on the application file.
- Auto-wire your sender and implement the run method to invoke sender.send method, which will post a message to the queue.
@SpringBootApplication
public class SpringRabbitMqApplication implements CommandLineRunner {
@Autowired
Sender sender;
public static void main(String[] args) {
SpringApplication.run(SpringRabbitMqApplication.class, args);
}
public void run(String... args) throws Exception {
sender.send("Messaging from App...!!!!");
}
}
- Creating a project: Select web // Web and Security / Spring Security
- POM will have a new entry
<artifactId>spring-boot-starter-security</artifactId>
- Use the same get mapping as earlier projects, run the application.
- While accessing the application through browser it will ask for user name and password.
Default username is user the default password will be available in console while starting the application.
- Username and password can be configured in properties file as well.
//This is an application.properties entry
spring.security.user.name=delta
spring.security.user.password=alpha
- Securing microservices is necessary when multiple microservices are interacting with each other.
- Spring provides security with no added overhead.
- Entities involved in OAuth are Client, Resource Owner, Authorization Server, Resource.
- Resource is a protected API.
- The owner of this API is considered as resource owner.
- Client is the one who needs the resource for performing a business function.
- Authorization server Validates the access keys and let the client use the resource.
- Access key or authentication token is an encrypted key which the authorization server verify to provide access to resource.
- Validation of access keys are done for each client to server request.
- Request and response sent back and forth depends on the grant-types
- OAuth 2 provides several "grant types" for different use cases. The grant types defined are:
- Authorization Code for apps running on a web server, browser-based and mobile apps
- Password for logging in with a username and password (only for first-party apps)
- Client credentials for application access without a user present
- Implicit was previously recommended for clients without a secret, but has been superseded by using the Authorization Code grant with PKCE.
OAuth Grant Types are explained in the Markdown OAuth_Explained.md
OAuth2 examples need to be revisited
- Browsers generally restrict client-side applications to fetch data from another server or domain.
- Enabling cross origin access is generally termed as CORS (Cross Origin Resource Sharing)
- Spring boot allows this by an annotation
@CrossOrigin
@RestController
public class AppCoontroller {
@CrossOrigin
@GetMapping("/")
public Greet displayMessage() {
return new Greet("Hi There");
}
}
- We can also add a specific origin as
@CrossOrigin("http://myapp.site")
- GLobal CORS can be enabled by using the
WebMvcConfigurer
bean, and customizing theaddCorsMapping(CorsRegistry registry method)
- Actuators provide out of the box mechanism for monitoring and managing spring boot microservices in production.
- Create a spring starter project with spring web, Spring HAL, Spring Hateoas.
- Add
management.endpoints.web.exposure.include=*
in application.properties - Actuator endpoints will be exposed on the URI
/actuator
in the example project it will behttp://localhost:9092/actuator
- Metrics can be accessed from Jconsole as well.
- Can be done by overriding the methods in
HealthIndicator
- Create a counter class to count the number of hits for a service.
- Create a health indicator class, which will implement the health method from health-indicator.
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class HealthMonitor implements HealthIndicator {
TPSCounter counter;
public Health health() {
boolean health = counter.isWeak();
if(health) {
return Health.outOfService().withDetail("Number of Hits were High", counter.count.toString()).build();
}
return Health.up().build();
}
//To increase the service hits after every call is made
public void updateTx() {
if(counter == null || counter.isExpired()) {
counter = new TPSCounter();
}
counter.increment();
}
}
- TPS counter should be able to deal with the increment of the counter every time service is invoke.
import java.util.Calendar;
import java.util.concurrent.atomic.LongAdder;
public class TPSCounter {
LongAdder count;
int maxServiceHits = 2;
Calendar expiry = null;
TPSCounter(){
this.count = new LongAdder();
this.expiry = Calendar.getInstance();
this.expiry.add(Calendar.MINUTE, 1);
}
boolean isExpired() {
return Calendar.getInstance().after(expiry);
}
boolean isWeak() {
return (count.intValue() > maxServiceHits);
}
//Everytime the method is invoked the count will increase by 1
void increment() {
count.increment();
}
}
> Health status can be checcked by the URI /actuator/health
> After the max hit is reached, the health will reach to out of service.