Skip to content

Service Discovery

Overview

The Service Discovery module provides a mechanism for services to find and communicate with each other in a dynamic, distributed environment. Instead of hardcoding IP addresses or hostnames, services register themselves with a central registry (like HashiCorp Consul) and query it to discover the locations of other services.

This is a fundamental pattern for building scalable and resilient microservice architectures. Athomic uses this module primarily for the Distributed Workload Sharding pattern.

How It Works

The ServiceDiscoveryProtocol defines the contract for any provider, with register, deregister, and discover methods. The ConsulDiscoveryProvider is the default implementation.

At startup, a service instance calls discovery_service.register_self(), which registers its service name, instance ID, and address with Consul. On shutdown, it calls deregister_self(). Other services can then call discovery_service.discover("service-name") to get a list of all healthy, active instances of that service.

API Reference

nala.athomic.integration.discovery.protocol.ServiceDiscoveryProtocol

Bases: Protocol

Defines the abstract contract for any service discovery provider.

This protocol adheres to the Dependency Inversion Principle (DIP), decoupling service consumers (like the Sharding or HTTP Client modules) from the concrete implementation (e.g., Consul, ZooKeeper).

deregister(service_id) async

Removes this service instance from the discovery provider.

Parameters:

Name Type Description Default
service_id str

The unique ID of the service instance to deregister.

required

Returns:

Name Type Description
bool bool

True if deregistration was successful.

discover(name) async

Discovers all healthy instances of a service by its logical name.

The provider should only return services that have a passing health check.

Parameters:

Name Type Description Default
name str

The logical name of the service to discover.

required

Returns:

Type Description
List[Dict[str, Any]]

List[Dict[str, Any]]: A list of dictionaries, where each dictionary represents a healthy service instance (containing address, port, etc.).

register(name, service_id, port, address=None, tags=None, health_check=None) async

Registers the current service instance with the discovery provider.

Parameters:

Name Type Description Default
name str

The logical name of the service (e.g., "user-service").

required
service_id str

A unique ID for this specific instance of the service.

required
port int

The port on which the service is running.

required
address Optional[str]

The address of the service. If None, the agent's address is used.

None
tags Optional[List[str]]

A list of tags to associate with the service.

None
health_check Optional[Dict[str, Any]]

A dictionary defining a health check (e.g., HTTP, TCP, TTL).

None

Returns:

Name Type Description
bool bool

True if registration was successful, False otherwise.

nala.athomic.integration.discovery.providers.consul_provider.ConsulDiscoveryProvider

Bases: ServiceDiscoveryProtocol

Service Discovery implementation using HashiCorp Consul as the backend.

This provider fulfills the ServiceDiscoveryProtocol contract by delegating I/O operations (register, deregister, discover) to the central, lifecycle-managed ConsulClient instance. All operations are wrapped for resilience via @cancellable_operation.

Attributes:

Name Type Description
settings DiscoverySettings

The service discovery configuration.

consul_client ConsulClient

The shared, active Consul client instance.

__init__(settings=None, consul_client=None)

Initializes the provider, resolving the shared ConsulClient dependency.

Parameters:

Name Type Description Default
settings Optional[DiscoverySettings]

Configuration for the discovery module.

None
consul_client Optional[ConsulClient]

Optional injected Consul client instance for testing.

None

deregister(service_id) async

Removes the service instance from the Consul agent.

Parameters:

Name Type Description Default
service_id str

The unique ID of the service instance to deregister.

required

Returns:

Name Type Description
bool bool

True if deregistration was successful.

discover(name) async

Discovers all currently healthy instances of a service by its logical name.

Parameters:

Name Type Description Default
name str

The logical name of the service to discover.

required

Returns:

Type Description
List[Dict[str, Any]]

List[Dict[str, Any]]: A list of dictionaries, each representing a healthy service instance.

register(name, service_id, port, address=None, tags=None, health_check=None) async

Registers the current service instance with the Consul agent.

Parameters:

Name Type Description Default
name str

The logical name of the service (e.g., "user-service").

required
service_id str

A unique ID for this specific service instance.

required
port int

The port on which the service is running.

required
address Optional[str]

The routable address of the service.

None
tags Optional[List[str]]

A list of tags for categorization.

None
health_check Optional[Dict[str, Any]]

A dictionary defining a health check.

None

Returns:

Name Type Description
bool bool

True if registration was successful, False otherwise.