Service Discovery
In a microservices architecture, services need to locate each other dynamically. Instances start, stop, and scale constantly โ hardcoded URLs donโt work. Service Discovery solves this.
The Problem
Section titled โThe Problemโ# Static config (breaks when instances change)OrderService โ http://payment-service:8080 โ what if it moves?
# With service discoveryOrderService โ [ Service Registry ] โ current address of PaymentServiceTwo Patterns
Section titled โTwo PatternsโClient-Side Discovery
Section titled โClient-Side DiscoveryโThe client queries the registry and load-balances itself:
Client โโโบ Service Registry โโโบ gets list of healthy instances โโโ [10.0.1.5:8080, 10.0.1.6:8080]Client picks one and calls it directlyTools: Netflix Eureka, Consul (with client-side library)
Server-Side Discovery
Section titled โServer-Side DiscoveryโThe client calls a load balancer; it queries the registry:
Client โโโบ Load Balancer โโโบ Service Registry โโโ picks instance โโโบ forwards request to instanceTools: AWS ALB, Kubernetes Services, NGINX with Consul
Consul Example
Section titled โConsul ExampleโRegister a service:
{ "service": { "name": "payment-service", "id": "payment-service-1", "address": "10.0.1.5", "port": 8080, "check": { "http": "http://10.0.1.5:8080/health", "interval": "10s", "timeout": "3s" } }}Query for healthy instances:
curl http://localhost:8500/v1/health/service/payment-service?passing=true.NET integration with Consul:
dotnet add package Consulbuilder.Services.AddSingleton<IConsulClient>(sp => new ConsulClient(cfg => cfg.Address = new Uri("http://consul:8500")));
// Register on startupvar registration = new AgentServiceRegistration{ ID = $"payment-service-{Environment.MachineName}", Name = "payment-service", Address = "10.0.1.5", Port = 8080, Check = new AgentServiceCheck { HTTP = "http://10.0.1.5:8080/health", Interval = TimeSpan.FromSeconds(10) }};await consulClient.Agent.ServiceRegister(registration);Kubernetes DNS (Built-in Discovery)
Section titled โKubernetes DNS (Built-in Discovery)โKubernetes provides service discovery out of the box via DNS. Every Service gets a DNS name:
apiVersion: v1kind: Servicemetadata: name: payment-service namespace: defaultspec: selector: app: payment ports: - port: 80 targetPort: 8080Other services can now reach it at:
http://payment-service.default.svc.cluster.local# or within the same namespace simply:http://payment-serviceIn .NET configuration:
{ "Services": { "PaymentService": "http://payment-service" }}Health Checks
Section titled โHealth ChecksโServices must report health so the registry stops routing to unhealthy instances:
builder.Services.AddHealthChecks() .AddSqlServer(connectionString) .AddRedis(redisConnectionString);
app.MapHealthChecks("/health");app.MapHealthChecks("/health/ready", new HealthCheckOptions{ Predicate = check => check.Tags.Contains("ready")});Comparison
Section titled โComparisonโ| Approach | Pros | Cons |
|---|---|---|
| Kubernetes DNS | Zero config, built-in | Only works in Kubernetes |
| Consul | Works anywhere, rich health checks | Operational overhead |
| Eureka | Good .NET/Java support | Less active development |
| AWS Cloud Map | Native AWS integration | AWS only |