Building Microservices with Spring Boot and Service Discovery
Microservices Architecture Overview
Microservices architecture structures an application as a collection of loosely coupled services, where each service implements a specific business capability and can be developed, deployed, and scaled independently. Spring Boot provides comprehensive support for building microservices in the Java ecosystem, including service registration, discovery, and inter-service communication.
Setting Up Service Discovery with Eureka
Service discovery allows microservices to locate each other without hardcoding network locations. Netflix Eureka serves as a central registry where services can register themselves and query for other available services.
Eureka Server Configuration
package com.example.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaRegistryApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaRegistryApplication.class, args);
}
}
Add the following configuration in application.yml:
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
Service Provider Implementation
The service provider registers with Eureka and exposes REST endpoints that other services can consume.
package com.example.productservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableDiscoveryClient
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}
@RestController
class ProductController {
@GetMapping("/products/{id}")
public Product getProduct(@PathVariable Long id) {
return new Product(id, "Sample Product", 99.99);
}
record Product(Long id, String name, double price) {}
}
Configure the provider in application.yml:
server:
port: 8081
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
Service Consumer Implementation
The consumer uses the discovered service through a load-balanced client.
package com.example.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestClient;
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
@Bean
RestClient.Builder restClientBuilder() {
return RestClient.builder();
}
}
package com.example.orderservice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClient;
@RestController
class OrderController {
private final RestClient restClient;
public OrderController(RestClient.Builder builder) {
this.restClient = builder.baseUrl("http://product-service").build();
}
@GetMapping("/orders/{orderId}")
public OrderResponse getOrder(@PathVariable Long orderId) {
var product = restClient.get()
.uri("/products/1")
.retrieve()
.body(Product.class);
return new OrderResponse(orderId, product);
}
record Product(Long id, String name, double price) {}
record OrderResponse(Long orderId, Product product) {}
}
Key Benefits of Microservices
Independent Deployment: Services can be deployed and updated without coordinating with other teams or services.
Technology Flexibility: Teams can choose different programming languages, frameworks, or databases for individual services based on specific requirements.
Scalability: Each service scales independently based on its own load characteristics, optimizing resource utilization.
Fault Isolation: Failure in one service doesn't cascade to other services, improving overall system resilience.
Configuraton Files Summary
For a complete microservices setup, ensure each service has proper Eureka client configuration pointing to the registry server. The Eureka server itself should run independently before starting any client services.