How to build microservice with Spring Boot (short notes)

Kirill Rybkin2022-08-11

Designing the microservice architecture

Decomposing the business problem

The architect breaks the business problem into chunks that represent discrete domains of activity. These chunks encapsulate the business rules and the data logic associated with a particular part of the business domain. For example, an architect might look at a business flow that needs to be carried out by code and realize that it needs both customer and product information.

Establishing service granularity

It also involves teasing out the actual database tables the services will access and only allowing each service to access the tables in its specific domain. Too coarse-grained service:

  • service with too many responsibilities;
  • service that manages data across a large number of tables;
  • service with too many test cases; Too fine-grained:
  • The microservices in one part of the problem domain breed like rabbits;
  • Your microservices are heavily interdependent on one another;
  • Your microservices become a collection of simple CRUD services;

Defining the service interfaces

  • Embrace the REST philosophy;
  • Use URIs to communicate intent;
  • Use JSON for your requests and responses;
  • Use HTTP status codes to communicate results;

Deployment principles

  • A microservice should be self-contained;
  • A microservice should be configurable;
  • A microservice instance needs to be transparent to the client;
  • A microservice should communicate its health (Spring Boot Actuator);

Containers or virtual machines

  • Containers can run everywhere, which facilitates development and implementation and increases portability;
  • Containers provide the ability to create predictable environments that are entirely isolated from other applications;
  • Containers can be started and stopped faster than VMs, which makes them cloud-native feasible;
  • Containers are scalable and can be actively scheduled and managed to optimize resource utilization, increasing the performance and maintainability of the application running within;
  • We can realize a maximum number of applications on a minimum number of servers.

Spring Cloud Configuration Server

Software developers always should keep the application configuration separate from the code. In most scenarios, this means not using hardcoded values in the code. This principle avoid to recompiled and/or redeployed application after change configuration. Managing application configuration is critical for microservices running in the cloud because microservice instances need to be launched quickly. When manually configure a service to get it deployed, it leads to configuration drift, an unexpected outage, and a lag time in responding within the application intervention. Following four principle for managing config:

  • Segregate - separate the service configuration information from service;
  • Abstract - example use a REST-based JSON service to retrieve the application’s configuration data;
  • Centralize - Centralize the application configuration into few repositories as possible;
  • Harden - service must be available and redundant;

Service discovery

In any distributed architecture, we need to find the hostname or IP address of where a machine is located. This problem resolves by service discovery. Service discovery is critical to microservice because of:

  • Horizontal scaling - adding more instances of a service inside a cloud service and more containers;
  • Resiliency - ability to absorb the impact of problems within an architecture or service without affecting the business;

Requirements

  • Highly available
  • Peer-to-peer - cluster shares the state of a service discovery instance;
  • Load balanced - spread requests across all service instance;
  • Resilient - client cache service information locally, that allows gradual degradation of the service discovery feature so that if the service discovery service becomes unavailable, applications can still function;
  • Fault tolerant - handle unavailable service.

    The architecture of service discovery

  • Service registration - When a service comes online it registers its IP address with a service discovery agent;
  • Client lookup of service address - A service location can be looked up by a logical name from the service discovery agent;
  • Information sharing - Service discovery nodes share service instance health information with each other;
  • Health monitoring - If a service dies, the service discovery layer removes the IP of the “dead” instance;

Resiliency patterns

Easy to detect that service crashes, but detecting that poor performance and routing around it is extremely difficult.

Client-side resiliency patterns

  • Client-side load balancing. As a client-side load balancer caching the physical location of service instances. When a service consumer needs to call a service instance, the client-side load balancer returns a location from the pool of service locations it maintains. load balancer can detect if a service instance is throwing errors or behaving poorly. If the client-side load balancer detects if a service instance is throwing errors, it can remove that service instance from the pool of available service locations.
  • Circuit breaker. When a remote service is called, the circuit breaker monitors the call. If the calls take too long, the circuit breaker kills the call. The circuit breaker pattern also monitors all calls to a remote resource, and if enough calls fail, the circuit breaker preventing future requests to the failing remote resource.
  • Fallback processing. When a remote service call fails the user's calls is not throw exception, but retrieve data from another data source (more generalized as example).
  • Bulkheads. In that patterns you break the calls to remote resources into their own thread pools and reduce the risk that a problem with one poorly resource call will down the entire application.

Implementing a circuit breaker

  • Circuit breakers in our code is to monitor remote calls and avoid long waits on services. In these scenarios, the circuit breaker is in charge of killing those connections and monitoring if there are more failing or poorly behaving calls. This pattern then implements a fast fail and prevents future requests to a failing remote resource. "Circuit breaker"
  • Fallback processing. AS we can see circuit breakers is a middleman between caller and remote service, we have the opportunity to intercept a service failure and choose an alternative execution path.

Service routing with Spring Cloud Gateway

In a distributed architecture, there are critical behaviors such as security, logging, and tracking users across multiple service calls occur. To implement this functionality, we’ll want these attributes to be consistently enforced across all of our services without the need for each individual development team to build their own solution. While it’s possible to use a common library or framework to assist with building these capabilities directly in an individual service, doing so has these implications:

  • It’s challenging to implement these capabilities in each service consistently;
  • Pushing the responsibilities to implement cross-cutting concerns like security and logging down to the individual development teams greatly increases the odds that someone will not implement them properly or will forget to do them;
  • It’s possible to create a hard dependency across all our services; To solve these problems, we need to abstract these cross-cutting concerns into a service that can sit independently and act as a filter and router for all the microservice calls in our architecture. End now all services calls route throught gateway service? which cab accept all cross-cute functionality to the requests.

Event Driven

to be continue

Distributed tracing

to be continue

Deploying

to be continue

Language: en