How Kratos Integrates with Service Discovery and Registry Patterns (etcd, Consul)
Kratos treats service discovery and registration as pluggable components that implement the registry.Registrar and registry.Discovery interfaces, allowing seamless integration with etcd, Consul, or any custom backend via constructor injection.
Kratos is a popular open-source Go microservices framework that abstracts service discovery through clean interface contracts. The framework defines core registry contracts in registry/registry.go that decouple your application from specific infrastructure backends. This architecture enables you to swap between etcd, Consul, or other service registries without changing application logic.
Core Registry Interfaces
The foundation of Kratos service discovery lives in registry/registry.go. This file defines two primary contracts:
- Registrar: Handles service lifecycle with
RegisterandDeregistermethods. - Discovery: Handles service resolution with
GetServiceandWatchmethods.
Any implementation satisfying these interfaces can be injected into the application via the Registrar option defined in options.go. The ServiceInstance struct carries metadata including service name, version, endpoints, and metadata map.
etcd Integration
The etcd implementation resides in contrib/registry/etcd and provides a production-ready backend using etcd leases for TTL-based service health.
Constructor and Configuration
The etcd/registry.go file defines the Registry struct that implements both Registrar and Discovery. Construction occurs through etcd.New (lines 66‑78), which accepts a clientv3.Client and functional options:
etcdreg.New(cli,
etcdreg.Namespace("/myservices"),
etcdreg.RegisterTTL(10*time.Second),
)
Registration Mechanism
Registration creates a lease against the etcd cluster and stores the service payload under the key pattern /<namespace>/<service>/<id>. A background goroutine maintains the lease heartbeat to prevent expiration during healthy operation. Deregistration revokes the lease, causing immediate key deletion.
Discovery and Watching
The GetService method performs a prefix query against the etcd keyspace using the configured namespace. For real-time updates, etcd/watcher.go (lines 26‑44) implements the Watcher interface using clientv3.Watcher to stream etcd events and translate them into service instance updates.
Consul Integration
The Consul implementation lives in contrib/registry/consul and integrates with HashiCorp Consul's agent API for service registration and health checking.
Constructor and Client Wrapper
The consul/registry.go constructor consul.New (lines 12‑32) wraps a consul.Client provided by consul/client.go. The constructor accepts options to enable health checks, heartbeats, and automatic deregistration:
consulreg.New(cli,
consulreg.WithHealthCheck(true),
consulreg.WithHeartbeat(true),
consulreg.WithDeregisterCriticalServiceAfter(30),
)
Registration with Health Checks
Registration uses Consul's AgentServiceRegistration API with optional TCP or script-based health checks. The implementation registers a service ID with checks that Consul uses to determine service health. When configured with WithHeartbeat, the client sends periodic updates to maintain the "passing" status.
Discovery and Watching
The GetService implementation first checks an in-memory cache; if empty, it queries the Consul Catalog API via c.cli.Service. The consul/watcher.go forwards change notifications from an internal serviceSet that updates when the Consul query returns different service instances.
Application Integration Flow
Integrating either registry follows a consistent three-step pattern:
- Instantiate the registry using the appropriate contrib package (etcd or Consul).
- Pass the registry to
kratos.Newusing thekratos.Registraroption. - Run the application, which automatically invokes
Registeron startup andDeregisteron graceful shutdown.
Services can discover peers by accessing the registry directly through the Discovery interface to call GetService or Watch.
Practical Implementation Examples
etcd Service Registration
package main
import (
"context"
"time"
etcdreg "github.com/go-kratos/kratos/contrib/registry/etcd/v2"
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/log"
clientv3 "go.etcd.io/etcd/client/v3"
)
func main() {
// Build an etcd client
cli, _ := clientv3.New(clientv3.Config{
Endpoints: []string{"localhost:2379"},
DialTimeout: 5 * time.Second,
})
// Create the Kratos etcd registry
etcdRegistry := etcdreg.New(cli,
etcdreg.Namespace("/myservices"),
etcdreg.RegisterTTL(10*time.Second),
)
// Build the Kratos application, injecting the registry
app := kratos.New(
kratos.ID("order-srv"),
kratos.Name("order"),
kratos.Version("v1.0.0"),
kratos.Registrar(etcdRegistry),
kratos.Logger(log.NewStdLogger()),
)
// Run the app (registrar will auto-register on start)
if err := app.Run(); err != nil {
panic(err)
}
}
Consul Service Registration
package main
import (
"context"
"time"
consulreg "github.com/go-kratos/kratos/contrib/registry/consul/v2"
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/log"
consulapi "github.com/hashicorp/consul/api"
)
func main() {
// Consul API client
cli, _ := consulapi.NewClient(&consulapi.Config{
Address: "127.0.0.1:8500",
Scheme: "http",
})
// Consul registry with health checks
consulRegistry := consulreg.New(cli,
consulreg.WithHealthCheck(true),
consulreg.WithHeartbeat(true),
consulreg.WithDeregisterCriticalServiceAfter(30),
)
// Kratos app with Consul registrar
app := kratos.New(
kratos.ID("payment-srv"),
kratos.Name("payment"),
kratos.Version("v2.1.0"),
kratos.Registrar(consulRegistry),
kratos.Logger(log.NewStdLogger()),
)
if err := app.Run(); err != nil {
panic(err)
}
}
Watching for Service Changes
func watchOrders(ctx context.Context, r registry.Discovery) {
w, _ := r.Watch(ctx, "order")
for {
services, err := w.Next()
if err != nil {
// handle context cancellation or watch errors
return
}
// react to updated service list
fmt.Printf("order instances: %v\n", services)
}
}
Summary
- Kratos defines abstract Registrar and Discovery interfaces in
registry/registry.gothat decouple applications from specific service registries. - The etcd implementation uses lease-based registration with automatic heartbeats and prefix-based discovery with etcd watchers.
- The Consul implementation wraps the Consul agent API, supporting configurable health checks and catalog-based service resolution.
- Both implementations reside in the
contrib/directory and satisfy the same interfaces, enabling zero-code-change swapping between backends. - Registration occurs automatically on application startup when using the
kratos.Registraroption, while discovery usesGetServiceorWatchmethods.
Frequently Asked Questions
How do I switch from etcd to Consul in an existing Kratos application?
Switching registries requires only changing the constructor call and import path. Replace etcdreg.New with consulreg.New, pass the appropriate client configuration, and inject the new registry using kratos.Registrar. The application code that calls GetService or Watch remains unchanged because both implementations satisfy the same registry.Discovery interface.
What is the difference between the Registrar and Discovery interfaces?
The Registrar interface manages a service's own lifecycle in the registry, providing Register and Deregister methods that Kratos calls during startup and shutdown. The Discovery interface enables clients to locate other services, providing GetService for immediate lookup and Watch for receiving updates when service instances change. A single struct (like etcd.Registry or consul.Registry) typically implements both interfaces.
How does Kratos handle service health checking with these registries?
For etcd, Kratos creates a lease with a TTL and spawns a heartbeat goroutine that keeps the lease alive; if the process crashes, etcd automatically removes the expired key. For Consul, the implementation registers health checks (TCP, HTTP, or TTL) with the Consul agent; if checks fail or the service becomes critical, Consul marks the service as unhealthy and can automatically deregister it after a configurable timeout.
Can I implement a custom service registry for Kratos?
Yes. Implement the Registrar and Discovery interfaces defined in registry/registry.go, ensuring your type provides thread-safe Register, Deregister, GetService, and Watch methods. Place your implementation in your project or a separate module, then inject it using kratos.Registrar(yourRegistry) when building the application. This pattern allows integration with proprietary service discovery systems or databases.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →