Skip to main content

Really Simple Service Registry - Python Client

Project description

SRSR Python Client

Python client library for the srsr service registry.

The client automatically handles registration, heartbeat maintenance, and deregistration with the service registry server. It supports configurable heartbeat intervals and error handling for robust service discovery in microservice architectures.

Installation

# Base package
pip install srsrpy

# With Flask extension
pip install srsrpy[flask]

Configuration

Registry Address

The service registry address can be specified in three ways (in order of precedence):

  1. Explicitly in code: Pass the address to ServiceRegistryClient
  2. Environment variable: Set SRSR_REGISTRY_URL (e.g., export SRSR_REGISTRY_URL=http://localhost:4214)
  3. Default: Uses http://localhost:4214 if not specified
from srsrpy import ServiceRegistryClient

# Explicit address
client = ServiceRegistryClient('my-service', registry_address='http://registry:4214', service_address='http://localhost:3000')

# Use environment variable or default
client = ServiceRegistryClient('my-service', service_address='http://localhost:3000')

Service Port Specification Rules

When registering your service, the service's port can be specified in different ways:

  1. In the service address: service_address='http://localhost:3000'
  2. As a separate argument: service_port='3000' (without port in address)
  3. Omitted entirely: The service is registered without port information.

Important: Port cannot be specified in both the address and as a separate argument - this raises a ValueError.

from srsrpy import ServiceRegistryClient

# Valid: Port in service address
client = ServiceRegistryClient('my-service', service_address='http://localhost:3000')

# Valid: Port as separate argument (address without port)
client = ServiceRegistryClient('my-service', service_address='http://localhost', service_port='3000')

# Valid: Port as separate argument (no address)
client = ServiceRegistryClient('my-service', service_port='3000')

# Valid: No port specified
client = ServiceRegistryClient('my-service', service_address='http://localhost')

# Invalid: Port in both places - raises ValueError
client = ServiceRegistryClient('my-service', service_address='http://localhost:3000', service_port='3000')

Basic Usage

Using Environment Variables with from_env()

For cloud-native and containerized applications, you can configure the client entirely through environment variables using the from_env() factory method:

from srsrpy import ServiceRegistryClient

# Set environment variables (e.g., in your deployment config, Dockerfile, or .env)
# export SRSR_SERVICE_NAME=my-service
# export SRSR_REGISTRY_URL=http://registry:4214  # Optional, defaults to localhost:4214
# export SRSR_SERVICE_ADDRESS=http://localhost:3000  # Optional
# export SRSR_HEARTBEAT_INTERVAL=20  # Optional

def main():
    # Configuration comes entirely from environment variables
    with ServiceRegistryClient.from_env():
        # Your service runs here...
        run_service()

if __name__ == "__main__":
    main()

Environment Variables:

  • SRSR_SERVICE_NAME - Service name (required)
  • SRSR_REGISTRY_URL - Registry address (optional, defaults to http://localhost:4214)
  • SRSR_SERVICE_ADDRESS - Service address (optional)
  • SRSR_SERVICE_PORT - Service port (optional)
  • SRSR_HEARTBEAT_INTERVAL - Heartbeat interval in seconds (optional, defaults to 20)

Using Context Manager (Recommended)

The recommended approach is to use the context manager pattern, which automatically handles registration and deregistration:

from srsrpy import ServiceRegistryClient

def main():
    # Using 'with' automatically registers on entry and deregisters on exit
    with ServiceRegistryClient(
        'my-service',                           # Service name (required)
        registry_address='http://localhost:4214',  # Registry server address (optional)
        service_address='http://localhost:3000'    # This service's address (optional)
    ):
        # Your service runs here, heartbeats are sent in the background
        run_service()

    # Deregistration happens automatically, even if an exception occurs

if __name__ == "__main__":
    main()

The context manager will:

  • Register with the service registry when entering the with block
  • Raise a RuntimeError if registration fails
  • Automatically deregister when exiting the block (even if an exception occurs)
  • Not suppress any exceptions that occur within the block

Manual Registration (Alternative)

You can also manage registration manually if needed:

from srsrpy import ServiceRegistryClient

def main():
    # Connect to registry at localhost:4214, register this service at localhost:3000
    client = ServiceRegistryClient(
        'my-service',                           # Service name (required)
        registry_address='http://localhost:4214',  # Registry server address (optional)
        service_address='http://localhost:3000'    # This service's address (optional)
    )

    # Register and start automatic heartbeats
    success = client.register()
    if not success:
        print("Failed to register with service registry")
        return

    try:
        # Your service runs here...
        # Heartbeats are sent automatically every 20 seconds
        run_service()
    finally:
        # Always deregister on shutdown
        client.deregister()

if __name__ == "__main__":
    main()

Port-Only Registration

If your service cannot determine its full address, you can register with just a port:

from srsrpy import ServiceRegistryClient

# The server will deduce the client IP and use http scheme
client = ServiceRegistryClient(
    'my-service',                           # Service name
    registry_address='http://localhost:4214',  # Registry server (optional)
    service_port='3000'                     # Just the port - server deduces IP
)

success = client.register()

Advanced Configuration

from srsrpy import ServiceRegistryClient

def heartbeat_error_handler(error):
    print(f"Service registry heartbeat failed: {error}")

def main():
    client = ServiceRegistryClient(
        'my-service',                               # Service name
        registry_address='http://localhost:4214',   # Registry server (optional)
        service_address='http://localhost:3000',    # Service address (optional)
        heartbeat_interval=10,                      # Custom interval in seconds (default: 20)
        heartbeat_error_handler=heartbeat_error_handler  # Optional error callback
    )

    success = client.register()
    if success:
        # Service logic here...
        pass

Configuration Options

heartbeat_interval (int)

Sets how often heartbeats are sent to the registry in seconds. Default is 20 seconds.

heartbeat_error_handler (callable)

Sets an optional callback function that will be called whenever heartbeat requests fail. The handler receives the error that occurred. By default, heartbeat failures are silently ignored.

# Example: Log heartbeat failures and update metrics
def error_handler(error):
    logger.error(f"Registry heartbeat failed: {error}")
    metrics.increment_counter("registry_heartbeat_failures")

client = ServiceRegistryClient(
    'my-service',
    registry_address='http://localhost:4214',
    service_address='http://localhost:3000',
    heartbeat_error_handler=error_handler
)

Interface

register() -> bool

Registers the service with the registry and starts automatic heartbeat maintenance. Returns True if registration succeeds, False otherwise.

deregister() -> None

Stops heartbeat maintenance and deregisters the service from the registry. Safe to call multiple times.

Error Handling

The client handles various error conditions gracefully:

  • Network failures during registration: Registration returns False
  • Network failures during heartbeat: By default, silently ignored (service will timeout and be deregistered). Optionally surfaced through heartbeat_error_handler
  • Invalid responses: Handled appropriately (e.g., missing ID field)
  • Connection errors: Caught and handled without crashing the application

Heartbeat Error Handling

By default, heartbeat failures are silently ignored to prevent noisy logging. However, you can provide a custom error handler to:

  • Log heartbeat failures for debugging
  • Update metrics or monitoring systems
  • Take application-specific actions

Signal Handler Example

For graceful shutdown, the context manager pattern automatically handles cleanup. If you need to preserve existing signal handlers for manual registration:

import signal
from srsrpy import ServiceRegistryClient

client = ServiceRegistryClient(
    'my-service',
    registry_address='http://localhost:4214',
    service_address='http://localhost:3000'
)

success = client.register()
if success:
    # Save the original handler and set up graceful shutdown
    prev_handler = signal.getsignal(signal.SIGINT)

    def handle_sigint(sig, frame):
        client.deregister()

        # Call the original handler if it existed
        if prev_handler:
            prev_handler(sig, frame)

    signal.signal(signal.SIGINT, handle_sigint)

Note: When using the context manager pattern, cleanup is automatic and signal handlers are typically not needed for deregistration.

Flask Integration

For Flask applications, srsrpy provides an extension that automatically handles service registration and deregistration with Flask's application lifecycle.

Installation

pip install srsrpy[flask]

Usage

The Flask extension uses environment variables for configuration and automatically registers your Flask application with the service registry on startup and deregisters on shutdown.

from flask import Flask
from srsrpy.contrib.flask import FlaskServiceRegistry

app = Flask(__name__)
FlaskServiceRegistry(app)

@app.route('/')
def hello():
    return 'Hello from registered service!'

if __name__ == '__main__':
    app.run()

Or with the application factory pattern:

from flask import Flask
from srsrpy.contrib.flask import FlaskServiceRegistry

registry = FlaskServiceRegistry()

def create_app():
    app = Flask(__name__)
    registry.init_app(app)

    @app.route('/')
    def hello():
        return 'Hello from registered service!'

    return app

if __name__ == '__main__':
    app = create_app()
    app.run()

Configuration

Set environment variables before starting your Flask application:

export SRSR_SERVICE_NAME=my-flask-api
export SRSR_REGISTRY_URL=http://registry:4214  # Optional
export SRSR_SERVICE_ADDRESS=http://localhost:5000  # Optional
export SRSR_HEARTBEAT_INTERVAL=20  # Optional

Graceful Degradation: If SRSR_SERVICE_NAME is not set, the extension silently does nothing, allowing your Flask app to run without service registry integration.


Roadmap

See ROADMAP.md for planned improvements and future enhancements.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

srsrpy-1.1.0.tar.gz (11.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

srsrpy-1.1.0-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

File details

Details for the file srsrpy-1.1.0.tar.gz.

File metadata

  • Download URL: srsrpy-1.1.0.tar.gz
  • Upload date:
  • Size: 11.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for srsrpy-1.1.0.tar.gz
Algorithm Hash digest
SHA256 a7c0bb23cbf00ca3c4b618702b49d305b25e1168c0c05e7aa201a277a339bf25
MD5 fcc47efd9fde9efe8b8ab80b610ca935
BLAKE2b-256 fb3a344b1fe5a001b5bd9f1933dd2b225433eba5eb96c70738db6c1139b33ea4

See more details on using hashes here.

Provenance

The following attestation bundles were made for srsrpy-1.1.0.tar.gz:

Publisher: srsrpy-release.yml on ifIMust/srsrpy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file srsrpy-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: srsrpy-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for srsrpy-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cb8d76155c8e1a353cf1be71a94742ace56e55b9ef81ce8c63850280e319f36d
MD5 78210a18390d5840803e2ef5802fcf69
BLAKE2b-256 2c55a79699902349d9abf50e81b30f6ab394555587b1145106ea3f2fbf384ab4

See more details on using hashes here.

Provenance

The following attestation bundles were made for srsrpy-1.1.0-py3-none-any.whl:

Publisher: srsrpy-release.yml on ifIMust/srsrpy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page