Skip to main content

A lightweight event bus implementation using Redis

Project description

Eve Bus

A lightweight, reliable event bus implementation using Redis as the message broker. This library provides a simple interface for publishing and subscribing to events in distributed systems, enabling efficient communication between components in microservice architectures.

Features

  • Simple yet powerful API: Intuitive interface for publishing and subscribing to events
  • Redis-backed: Leverages Redis' pub/sub functionality for robust message delivery
  • Thread-safe implementation: Safe for use in multi-threaded environments
  • Multiple handlers per event: Support for registering multiple functions to handle the same event
  • Automatic retry mechanism: Built-in support for retrying failed event handlers
  • Clean shutdown: Proper resource management and graceful shutdown
  • Dependency injection ready: Easy integration with popular DI frameworks
  • Distributed system friendly: Ideal for communication between services in microservice architectures
  • Type-safe events: Leverages Python's type hinting for event data structures
  • Minimal dependencies: Lightweight with few external dependencies

Architecture Highlights

Eve Bus follows a clean architecture approach with clear separation of concerns:

  • Domain layer: Defines core event models
  • Ports layer: Specifies abstract interfaces for event publishing and subscription
  • Adapters layer: Implements the ports using Redis as the underlying technology

This architecture ensures loose coupling between your application code and the event bus implementation, making it easy to swap out the underlying message broker if needed.

Installation

pip install eve-bus

Usage

Basic Setup

from redis import Redis
from eve.core import RedisEventBus, subscribe, publish, set_event_bus, Event

# Create a Redis client
redis_client = Redis(host='localhost', port=6379, db=0)

# Create an event bus instance
event_bus = RedisEventBus(redis_client)

# Set the global event bus instance (required for using @subscribe decorator)
set_event_bus(event_bus)

# Define an event class
class UserCreated(Event):
    user_id: str
    username: str
    email: str

# Subscribe to an event
@subscribe('UserCreated')
def handle_user_created(event_data):
    print(f"New user created: {event_data['username']} ({event_data['email']})")
    print(f"User ID: {event_data['user_id']}")

# Publish an event
user_created_event = UserCreated(user_id='123', username='john_doe', email='john@example.com')
publish(user_created_event)

# When done, shutdown the event bus to clean up resources
event_bus.shutdown()

Using with Dependency Injection

Eve Bus integrates seamlessly with dependency injection frameworks like dependency_injector:

from dependency_injector import containers, providers
from redis import Redis
from eve.core import RedisEventBus, subscribe, set_event_bus, Event

class Container(containers.DeclarativeContainer):
    # Redis client provider
    redis_client = providers.Singleton(
        Redis,
        host='localhost',
        port=6379,
        db=0
    )

    # Event bus provider
    event_bus = providers.Singleton(RedisEventBus, redis_client=redis_client)

# Define a service that uses the event bus
class UserService:
    def __init__(self, event_bus: RedisEventBus):
        self.event_bus = event_bus
    
    def create_user(self, user_id: str, username: str, email: str):
        # Create user logic here
        print(f"Creating user: {username}")
        
        # Publish user created event
        event = UserCreated(user_id=user_id, username=username, email=email)
        self.event_bus.publish(event)
        
        return {"user_id": user_id, "username": username}

# Add the service to the container
Container.user_service = providers.Factory(UserService, event_bus=Container.event_bus)

# Create a container instance
container = Container()

# Get the event bus instance and set it globally
set_event_bus(container.event_bus())

# Get the user service
user_service = container.user_service()

# Use the service
user_service.create_user("456", "jane_doe", "jane@example.com")

Integration with FastAPI

Here's how to integrate Eve Bus with FastAPI:

from fastapi import FastAPI, Depends, BackgroundTasks
from redis import Redis
from eve.core import RedisEventBus, subscribe, publish, set_event_bus, Event
from pydantic import BaseModel
import uvicorn

# Define event models
class OrderCreated(Event):
    order_id: str
    user_id: str
    total_amount: float

class PaymentReceived(Event):
    payment_id: str
    order_id: str
    amount: float

# FastAPI app instance
app = FastAPI(title="Eve Bus with FastAPI")

# Redis and Event Bus setup
class EventBusManager:
    _instance: RedisEventBus = None
    
    @classmethod
    def get_event_bus(cls):
        if cls._instance is None:
            redis_client = Redis(host='localhost', port=6379, db=0)
            cls._instance = RedisEventBus(redis_client)
            set_event_bus(cls._instance)
        return cls._instance

    @classmethod
    def shutdown(cls):
        if cls._instance:
            cls._instance.shutdown()
            cls._instance = None

    # Dependency to get the event bus
    @classmethod
    def get_event_bus(cls):
        return cls._instance

# Pydantic models for API requests
class CreateOrderRequest(BaseModel):
    user_id: str
    items: list
    total_amount: float

# API endpoint to create an order
@app.post("/orders/")
async def create_order(
    request: CreateOrderRequest,
    event_bus: RedisEventBus = Depends(get_event_bus)
):
    # Generate order ID (in real app, use a proper ID generation strategy)
    order_id = f"order_{hash(request.user_id + str(request.total_amount))}"
    
    # Create and publish the order created event
    event = OrderCreated(
        order_id=order_id,
        user_id=request.user_id,
        total_amount=request.total_amount
    )
    event_bus.publish(event)
    
    return {"order_id": order_id, "status": "created"}

# API endpoint to process payment
@app.post("/payments/")
async def process_payment(
    order_id: str,
    amount: float,
    background_tasks: BackgroundTasks,
    event_bus: RedisEventBus = Depends(get_event_bus)
):
    # Process payment logic here
    payment_id = f"payment_{hash(order_id + str(amount))}"
    
    # Publish payment received event using background task
    background_tasks.add_task(
        event_bus.publish,
        PaymentReceived(payment_id=payment_id, order_id=order_id, amount=amount)
    )
    
    return {"payment_id": payment_id, "status": "processing"}

# Event handler for order creation
@subscribe("OrderCreated")
def handle_order_created(event_data):
    print(f"New order received: {event_data['order_id']}")
    print(f"User ID: {event_data['user_id']}")
    print(f"Total amount: ${event_data['total_amount']}")
    # Here you could update inventory, send notifications, etc.

# Event handler for payment processing
@subscribe("PaymentReceived")
def handle_payment_received(event_data):
    print(f"Payment received: {event_data['payment_id']}")
    print(f"For order: {event_data['order_id']}")
    print(f"Amount: ${event_data['amount']}")
    # Here you could update order status, generate invoice, etc.

# Shutdown event bus on app exit
@app.on_event("shutdown")
async def shutdown_event_bus():
    event_bus = EventBusManager.get_event_bus()
    event_bus.shutdown()

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

Configuration

Eve Bus can be configured using environment variables. Create a .env file in your project root or set these variables in your environment:

Core Configuration

  • EVENT_CHANNEL: Prefix for Redis channels (default: 'event') This prefix is used when creating Redis channels for event communication.

Redis Configuration

  • REDIS_HOST: Redis server hostname or IP address (default: 'localhost')
  • REDIS_PORT: Redis server port (default: 6379)
  • REDIS_DB: Redis database number to use (default: 0)
  • REDIS_PASSWORD: Redis server password (optional, default: None)

Example .env File

# Eve Bus Configuration
EVENT_CHANNEL=my_app_events

# Redis Configuration
REDIS_HOST=redis.example.com
REDIS_PORT=6379
REDIS_DB=2
REDIS_PASSWORD=my_secure_password

For advanced usage, you can also configure the Redis client directly when creating the RedisEventBus instance, overriding any environment variables:

Best Practices

When using Eve Bus in your applications, consider these best practices:

  1. Define clear event structures: Create well-defined event classes that clearly represent the data being communicated
  2. Use meaningful event names: Choose descriptive names for your events to make the system easier to understand
  3. Handle failures gracefully: Implement error handling in your event handlers
  4. Keep handlers focused: Each event handler should perform a single responsibility
  5. Monitor event processing: Add logging to track event publishing and handling
  6. Always shutdown: Call event_bus.shutdown() when your application exits to clean up resources

Advanced Usage

Batch Processing

Eve Bus includes support for batching events, which can improve performance in high-throughput scenarios.

Error Handling and Retries

You can implement custom retry logic for failed event handlers to improve system resilience.

Event Filtering

Implement event filtering to process only the events that are relevant to your application components.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

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

eve_bus-0.1.2.tar.gz (16.2 kB view details)

Uploaded Source

Built Distribution

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

eve_bus-0.1.2-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

File details

Details for the file eve_bus-0.1.2.tar.gz.

File metadata

  • Download URL: eve_bus-0.1.2.tar.gz
  • Upload date:
  • Size: 16.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.6

File hashes

Hashes for eve_bus-0.1.2.tar.gz
Algorithm Hash digest
SHA256 a9c13479f299cd536b981fab1a7403710cf37af7a1412472c6627d3abe9100d4
MD5 1ecb9ba34f41492a68e1e41afac92c20
BLAKE2b-256 22e7689b1d3c3a95a061a52a7ea092e6410147ea78da05df981b63cb483af690

See more details on using hashes here.

File details

Details for the file eve_bus-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: eve_bus-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 13.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.6

File hashes

Hashes for eve_bus-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 7c0a591b5e7e54f5ddb33dfe711f1fc979d68d3a1e5b40eef4a8015204bd18a5
MD5 4447a6e8a750a200c312f3622c034ea7
BLAKE2b-256 3b2fe19221bb71fb8bbd4b1abb2c895884b063cd41ea90d0203ff0381a4fbd7a

See more details on using hashes here.

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