Building Scalable gRPC Microservices with Spring Cloud: Eureka vs Consul
By Ercan - 28/10/2025
Modern microservices architectures demand scalable, resilient service discovery. In this post, I’ll walk you through a real-world implementation using Spring Boot, Spring Cloud, and gRPC, comparing two popular service registries: Eureka and Consul.
Architecture Overview
This project is structured as a multi-module Maven application, each module serving a distinct role:
- config-server: Centralized configuration using Spring Cloud Config.
 - grpc-lib: Contains the gRPC service definition (
hello.proto) and generated stubs. - grpc-server: Implements the 
HelloServicegRPC service. - grpc-client: Exposes a REST endpoint and forwards requests to 
grpc-servervia gRPC. - gateway: Spring Cloud Gateway that exposes only the 
grpc-clientto external traffic. - service registry: Either Eureka (embedded) or Consul (standalone via Docker Compose).
 
🧭 This project is part of a comparative series. You can explore both implementations on GitHub:
- Consul-based version: spring-cloud-grpc-concept-consul
 - Eureka-based version: spring-cloud-grpc-concept-eureka
 
🔁 Request Flow
- A REST request hits the Gateway.
 - Gateway forwards it to 
grpc-client. grpc-clientdiscoversgrpc-serverinstances via Eureka or Consul.- It sends a gRPC request to one of the servers.
 - The response is returned to the original caller.
 
Sample request:
curl --location 'http://localhost:8080/v1/grpc-client/hello?name=Ercan'
Sample response:
Hello Ercan from grpc-server:b4396685703dc335cc773531c378b838 over grpc-client:5c4ad9a3c23cb85795e3710d6c460c6b
Observability & Load Distribution
Each grpc-client and grpc-server instance generates a unique instance ID at startup. This ID is included in the response, allowing you to verify traffic distribution.
Although ports are fixed due to Docker Compose health checks, instance uniqueness is preserved via these IDs.
You can scale services with:
docker-compose up --scale grpc-server=3 --scale grpc-client=2
⚠️ Eureka vs Consul: Scaling Behavior
During local testing, I observed a critical difference:
- Eureka struggled when scaling 
grpc-clientandgrpc-serverinstances. Some instances failed to register properly, and service discovery became unreliable. - Consul, on the other hand, handled scaling gracefully. All instances registered and responded as expected, even under load.
 
This behavior aligns with architectural differences:
| Feature | Eureka | Consul | 
|---|---|---|
| Type | Java-based registry | Standalone agent (Go-based) | 
| Consistency Model | AP (Availability + Partition) | CP (Consistency + Partition) | 
| Health Checks | Basic metadata | Native TTL & HTTP checks | 
| Scaling Behavior | Fragile under high instance count | Stable and consistent | 
| UI & Monitoring | Minimal | Rich dashboard with KV store | 
| Integration | Spring Cloud Netflix | Spring Cloud Consul Discovery | 
🐳 Docker Compose Setup
All modules are dockerized. The stack is launched with:
docker-compose up --build
Consul runs as a standalone container, while Eureka is embedded in the project when used.
Conclusion
This project demonstrates how to build scalable gRPC microservices with Spring Cloud. While both Eureka and Consul offer service discovery, Consul proved more robust in multi-instance scenarios, especially in local environments with Docker Compose.
If you're exploring service discovery for gRPC-based systems, Consul might be the better choice for reliability and observability.
Tags: spring boot, spring cloud, grpc, eureka, consul, java
