Lab: Spring Cloud Microservices - Eureka & Feign

Lab Overview

In this lab, you will build a simple microservices architecture consisting of:

  1. A Eureka Server for service discovery
  2. Two microservices that communicate with each other using Feign

The scenario will be a basic e-commerce system with:

  • Product Service: manages product information
  • Order Service: creates orders and fetches product details from the Product Service

Let’s break this down into clear, manageable steps.

Lab Instructions

Prerequisites

  • Java 17 or higher
  • Maven or Gradle
  • IDE (IntelliJ IDEA, Eclipse, or VS Code)
  • Basic knowledge of Spring Boot

Step 1: Set up the Eureka Server

  1. Create a new Spring Boot project at Spring Initializer with the following:
  • Artifact: eureka-server
  • Dependencies: Eureka Server
  1. Open the main application class and add @EnableEurekaServer:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
  1. Configure the server in application.properties:
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
spring.application.name=eureka-server
  1. Run the application and visit http://localhost:8761 to see the Eureka dashboard.

Step 2: Create the Product Service

  1. Create a new Spring Boot project with:
  • Artifact: product-service
  • Dependencies: Spring Web, Eureka Discovery Client
  1. Configure the application in application.properties:
spring.application.name=product-service
server.port=8081
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
  1. Create a Product model class:
public class Product {
    private Long id;
    private String name;
    private String description;
    private double price;

    // Constructor, getters, setters
}
  1. Create a ProductController:
@RestController
@RequestMapping("/products")
public class ProductController {

    private Map<Long, Product> productMap = new HashMap<>();

    @PostConstruct
    public void setupProducts() {
        Product p1 = new Product(1L, "Laptop", "High performance laptop", 999.99);
        Product p2 = new Product(2L, "Phone", "Smartphone with great camera", 699.99);
        productMap.put(p1.getId(), p1);
        productMap.put(p2.getId(), p2);
    }

    @GetMapping
    public List<Product> getAllProducts() {
        return new ArrayList<>(productMap.values());
    }

    @GetMapping("/{id}")
    public Product getProduct(@PathVariable Long id) {
        return productMap.get(id);
    }
}
  1. Enable Eureka client in the main application class:
@SpringBootApplication
@EnableEurekaClient
public class ProductServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductServiceApplication.class, args);
    }
}

Step 3: Create the Order Service

  1. Create a new Spring Boot project with:
  • Artifact: order-service
  • Dependencies: Spring Web, Eureka Discovery Client, OpenFeign
  1. Configure the application in application.properties:
spring.application.name=order-service
server.port=8082
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
  1. Enable Eureka client and Feign clients in the main application class:
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}
  1. Create a Product model class (identical to the one in Product Service).

  2. Create an Order model class:

public class Order {
    private Long id;
    private Long productId;
    private int quantity;
    private double totalPrice;
    private Product product;

    // Constructor, getters, setters
}
  1. Create a Feign client to communicate with the Product Service:
@FeignClient(name = "product-service")
public interface ProductClient {

    @GetMapping("/products/{id}")
    Product getProduct(@PathVariable("id") Long id);
}
  1. Create an OrderController:
@RestController
@RequestMapping("/orders")
public class OrderController {

    private final ProductClient productClient;
    private final Map<Long, Order> orderMap = new HashMap<>();
    private Long nextOrderId = 1L;

    public OrderController(ProductClient productClient) {
        this.productClient = productClient;
    }

    @PostMapping
    public Order createOrder(@RequestBody Order order) {
        // Get product details from product-service
        Product product = productClient.getProduct(order.getProductId());

        // Set order details
        order.setId(nextOrderId++);
        order.setProduct(product);
        order.setTotalPrice(product.getPrice() * order.getQuantity());

        // Save order
        orderMap.put(order.getId(), order);
        return order;
    }

    @GetMapping("/{id}")
    public Order getOrder(@PathVariable Long id) {
        return orderMap.get(id);
    }

    @GetMapping
    public List<Order> getAllOrders() {
        return new ArrayList<>(orderMap.values());
    }
}

Step 4: Test the Microservices

  1. Start all three applications in this order:
  • Eureka Server
  • Product Service
  • Order Service
  1. Check the Eureka dashboard at http://localhost:8761 to confirm both services are registered.

  2. Test the Product Service:

  • GET http://localhost:8081/products
  • GET http://localhost:8081/products/1
  1. Test the Order Service by creating a new order:
  • POST http://localhost:8082/orders

    { "productId": 1, "quantity": 2 }
  • GET http://localhost:8082/orders

Lab Assignments

  1. Basic: Add another endpoint in the Product Service and access it from the Order Service.

  2. Intermediate: Add a third microservice (e.g., User Service) and integrate it with the existing services.

  3. Advanced: Implement circuit breaking with Resilience4j or Hystrix to handle failures gracefully.

Discussion Questions

  1. How does service discovery with Eureka simplify microservice architecture?
  2. What are the advantages of using Feign over RestTemplate?
  3. How would you handle a situation where the Product Service is down?
  4. What are some potential scaling challenges in this architecture?

Key Concepts to Highlight

  • Service Discovery: Allows services to find and communicate with each other without hardcoding hostnames and ports
  • Client-Side Load Balancing: Provided by Ribbon (integrated with Feign)
  • Declarative REST Client: Feign simplifies API calls between services
  • Microservice Communication: Synchronous communication patterns

This lab provides hands-on experience with core Spring Cloud components while demonstrating practical microservice patterns.


By Wahid Hamdi