Tools: Part 1 : Peer Aware Eureka Service Registry

Tools: Part 1 : Peer Aware Eureka Service Registry

Source: Dev.to

About the Order System Used in This Series ## What This Part Covers ## What Is a Service Registry? ## Why Start with Eureka? ## Let’s Begin 🚀 ## Why Service Discovery Is Mandatory in Production ## High-Level Architecture ## Existing Order System ## Step 1: Peer-Aware Eureka Service Registry (3 Nodes) ## Why Peer-Aware Eureka? ## Eureka Server Configuration ## Step 1: Peer-Aware Eureka Service Registry (3 Nodes) ## Why Peer-Aware Eureka? ## Eureka Server Configuration ## Eureka Server Application ## Step 2: Register Order System with Eureka ## Order System Configuration ## Step 3: Create Order Consumer Microservice ## Step 4: Service-to-Service Call ## Summary New Series: Spring Cloud Microservice Architecture (Service Discovery · Gateway · Nginx · Production Patterns) This blog marks the beginning of a new series focused entirely on Spring Cloud–based microservice architecture and production-grade system design. In this series, we will design and evolve a complete microservices ecosystem using: To avoid reinventing the wheel, this series reuses an existing microservice from my earlier work: 👉 Production-Grade Spring Boot API Design https://dev.to/pratik280/series/35113 The order-system from that series is used only as a sample business service so we can focus purely on: 📌 This Spring Cloud series is completely independent and does not require reading the previous series, although familiarity with the order-system API helps. This Part 1 focuses on: A Service Registry is a centralized system that keeps track of all running microservice instances in a distributed system. Instead of services communicating using fixed IP addresses and ports, they: 📌 In Spring Cloud, Eureka acts as the Service Registry. Before gateways, security, or scaling: Services must be able to find each other reliably. That is exactly what Eureka Service Registry solves — and it’s the foundation of every production-grade Spring Cloud system. In the next sections, we’ll set up a 3-node peer-aware Eureka cluster and use it as the backbone for all service communication in the system. In real production systems: Hardcoding endpoints like: http://localhost:8080/orders ❌ breaks scaling ❌ breaks failover ❌ tightly couples services From the earlier series, the order-system already exposes: Now we make this production-grade using service discovery. 👉 Minimum recommended: 3 nodes 👉 Minimum recommended: 3 nodes Note: This configuration represents one Eureka server instance. For a peer-aware Eureka cluster, create multiple Eureka applications (for example, 3 instances) and modify: so that each Eureka node points to all other Eureka nodes using their respective IPs and ports. What does @EnableEurekaServer do? This service consumes order-system data without knowing: What Is RestTemplate? RestTemplate is Spring’s synchronous HTTP client used for REST calls. @LoadBalanced enables: Load-Balanced RestTemplate Configuration Order Consumer Controller Key Point http://order-system/orders ✔ Logical service name ✔ Resolved dynamically via Eureka Order Consumer Configuration What Happens Internally? Why This Architecture Is Production-Grade Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: Client ↓ Nginx (Reverse Proxy & LB) ↓ Spring Cloud Gateway (multiple instances) ↓ Eureka Cluster (3 peer-aware nodes) ↓ Order System (multiple instances) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: Client ↓ Nginx (Reverse Proxy & LB) ↓ Spring Cloud Gateway (multiple instances) ↓ Eureka Cluster (3 peer-aware nodes) ↓ Order System (multiple instances) CODE_BLOCK: Client ↓ Nginx (Reverse Proxy & LB) ↓ Spring Cloud Gateway (multiple instances) ↓ Eureka Cluster (3 peer-aware nodes) ↓ Order System (multiple instances) CODE_BLOCK: GET http://localhost:8085/orders { "status": 200, "message": "All orders fetch successfully", "data": [ { "id": 1, "productName": "Mobile" }, { "id": 2, "productName": "Mobile" }, { "id": 3, "productName": "Mobile" }, { "id": 4, "productName": "Smartwatch" } ], "errors": null } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: GET http://localhost:8085/orders { "status": 200, "message": "All orders fetch successfully", "data": [ { "id": 1, "productName": "Mobile" }, { "id": 2, "productName": "Mobile" }, { "id": 3, "productName": "Mobile" }, { "id": 4, "productName": "Smartwatch" } ], "errors": null } CODE_BLOCK: GET http://localhost:8085/orders { "status": 200, "message": "All orders fetch successfully", "data": [ { "id": 1, "productName": "Mobile" }, { "id": 2, "productName": "Mobile" }, { "id": 3, "productName": "Mobile" }, { "id": 4, "productName": "Smartwatch" } ], "errors": null } CODE_BLOCK: spring.application.name=eureka-server server.port=8760 eureka.client.service-url.defaultZone=\ http://EurekaInstance1IP:8760/eureka,\ http://EurekaInstance2IP:8761/eureka,\ http://EurekaInstance3IP:8762/eureka eureka.client.register-with-eureka=true eureka.client.fetch-registry=true Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: spring.application.name=eureka-server server.port=8760 eureka.client.service-url.defaultZone=\ http://EurekaInstance1IP:8760/eureka,\ http://EurekaInstance2IP:8761/eureka,\ http://EurekaInstance3IP:8762/eureka eureka.client.register-with-eureka=true eureka.client.fetch-registry=true CODE_BLOCK: spring.application.name=eureka-server server.port=8760 eureka.client.service-url.defaultZone=\ http://EurekaInstance1IP:8760/eureka,\ http://EurekaInstance2IP:8761/eureka,\ http://EurekaInstance3IP:8762/eureka eureka.client.register-with-eureka=true eureka.client.fetch-registry=true CODE_BLOCK: spring.application.name=eureka-server server.port=8760 eureka.client.service-url.defaultZone=\ http://EurekaInstance1IP:8760/eureka,\ http://EurekaInstance2IP:8761/eureka,\ http://EurekaInstance3IP:8762/eureka eureka.client.register-with-eureka=true eureka.client.fetch-registry=true Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: spring.application.name=eureka-server server.port=8760 eureka.client.service-url.defaultZone=\ http://EurekaInstance1IP:8760/eureka,\ http://EurekaInstance2IP:8761/eureka,\ http://EurekaInstance3IP:8762/eureka eureka.client.register-with-eureka=true eureka.client.fetch-registry=true CODE_BLOCK: spring.application.name=eureka-server server.port=8760 eureka.client.service-url.defaultZone=\ http://EurekaInstance1IP:8760/eureka,\ http://EurekaInstance2IP:8761/eureka,\ http://EurekaInstance3IP:8762/eureka eureka.client.register-with-eureka=true eureka.client.fetch-registry=true CODE_BLOCK: @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } CODE_BLOCK: @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } CODE_BLOCK: spring.application.name=order-system eureka.client.service-url.defaultZone=\ http://localhost:8760/eureka,\ http://localhost:8761/eureka,\ http://localhost:8762/eureka eureka.instance.prefer-ip-address=true Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: spring.application.name=order-system eureka.client.service-url.defaultZone=\ http://localhost:8760/eureka,\ http://localhost:8761/eureka,\ http://localhost:8762/eureka eureka.instance.prefer-ip-address=true CODE_BLOCK: spring.application.name=order-system eureka.client.service-url.defaultZone=\ http://localhost:8760/eureka,\ http://localhost:8761/eureka,\ http://localhost:8762/eureka eureka.instance.prefer-ip-address=true CODE_BLOCK: ORDER-SYSTEM → [instance-1, instance-2, instance-3] Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: ORDER-SYSTEM → [instance-1, instance-2, instance-3] CODE_BLOCK: ORDER-SYSTEM → [instance-1, instance-2, instance-3] CODE_BLOCK: @Configuration public class RestConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: @Configuration public class RestConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } } CODE_BLOCK: @Configuration public class RestConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } } CODE_BLOCK: @RestController @RequestMapping("/consume") public class OrderConsumerController { private final RestTemplate restTemplate; public OrderConsumerController(RestTemplate restTemplate){ this.restTemplate = restTemplate; } @GetMapping("/orders") public String fetchOrders() { return restTemplate.getForObject( "http://order-system/orders", String.class ); } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: @RestController @RequestMapping("/consume") public class OrderConsumerController { private final RestTemplate restTemplate; public OrderConsumerController(RestTemplate restTemplate){ this.restTemplate = restTemplate; } @GetMapping("/orders") public String fetchOrders() { return restTemplate.getForObject( "http://order-system/orders", String.class ); } } CODE_BLOCK: @RestController @RequestMapping("/consume") public class OrderConsumerController { private final RestTemplate restTemplate; public OrderConsumerController(RestTemplate restTemplate){ this.restTemplate = restTemplate; } @GetMapping("/orders") public String fetchOrders() { return restTemplate.getForObject( "http://order-system/orders", String.class ); } } CODE_BLOCK: spring: application: name: order-consumer server: port: 8090 eureka: client: service-url: defaultZone: http://localhost:8760/eureka, http://localhost:8761/eureka, http://localhost:8762/eureka instance: prefer-ip-address: true Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: spring: application: name: order-consumer server: port: 8090 eureka: client: service-url: defaultZone: http://localhost:8760/eureka, http://localhost:8761/eureka, http://localhost:8762/eureka instance: prefer-ip-address: true CODE_BLOCK: spring: application: name: order-consumer server: port: 8090 eureka: client: service-url: defaultZone: http://localhost:8760/eureka, http://localhost:8761/eureka, http://localhost:8762/eureka instance: prefer-ip-address: true CODE_BLOCK: GET http://localhost:8090/consume/orders Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: GET http://localhost:8090/consume/orders CODE_BLOCK: GET http://localhost:8090/consume/orders - Spring Cloud Netflix Eureka (Service Discovery) - Spring Cloud Gateway (API Gateway) - Nginx (Edge reverse proxy & load balancer) - Production-ready scaling and fault-tolerance patterns - Service discovery - Inter-service communication - Load balancing - Infrastructure concerns - order-system : https://github.com/Pratik280/order-system - order-consumer : https://github.com/Pratik280/order-consumer - eureka-server : https://github.com/Pratik280/eureka-server - Why service discovery is mandatory in production - Why a single Eureka server is not enough - How to design a peer-aware Eureka cluster - How microservices register themselves using logical names - How this eliminates hardcoded IPs and ports - Register themselves with the registry - Discover other services dynamically using logical names - Services scale dynamically - Instances come and go - IPs and ports must never be hardcoded - Load balancing must be automatic - Failure of one node must not break the system - A single Eureka server is a single point of failure - In production, Eureka must be highly available - Each Eureka node replicates registry data with its peers - A single Eureka server is a single point of failure - In production, Eureka must be highly available - Each Eureka node replicates registry data with its peers - server.port - eureka.client.service-url.defaultZone - Turns a Spring Boot application into a Service Registry - Accepts service registrations from microservices - Tracks heartbeats to detect unhealthy instances - Replicates registry information to peer Eureka nodes - Run multiple instances of order-system - All instances register under one logical name: - Number of running instances - It only supports fixed URLs - It does not understand Eureka - Service name resolution - Instance lookup from Eureka - Client-side load balancing - Consumer calls http://order-system/orders - @LoadBalanced RestTemplate intercepts the call - Eureka returns all registered order-system instances - One instance is selected - Request is forwarded - Response is returned - No hardcoded IPs or ports - Automatic scaling - High availability - Fault tolerance - Clean separation of concerns - Gateway + Nginx hide internal topology - Eureka provides service discovery - Peer-aware Eureka removes SPOF - @EnableEurekaServer creates the registry - @LoadBalanced RestTemplate enables dynamic routing - Services communicate using logical names - Scaling requires no code changes