Service Container mit Dependency Injection, Lifecycle-Management und Resilience-Patterns.
from trixy_core.service import (
# Core
IService, ServiceContainer,
ServicePriority, ServiceGroup, ServiceState,
# Lazy Loading
LazyService, LazyProxy,
# Shutdown
GracefulShutdownManager, ShutdownConfig,
# Circuit Breaker
CircuitBreaker, CircuitBreakerConfig, CircuitState,
# Scopes
SingletonScope, TransientScope, RequestScope,
# Metrics
MetricsCollector, ServiceMetrics,
)
Abstrakte Basisklasse für alle Services.
from trixy_core.service import IService, ServicePriority, ServiceGroup
class MyService(IService):
NAME = "my_service"
PRIORITY = ServicePriority.OPTIONAL
GROUP = ServiceGroup.UTILITY
DEPENDENCIES = ["event_manager", "config_manager"]
async def on_pre_start(self) -> None:
"""Wird vor dem Start aufgerufen."""
self.logger.debug("Bereite Service vor")
async def on_start(self) -> None:
"""Hauptstart-Logik."""
self.logger.info("Service gestartet")
async def on_post_start(self) -> None:
"""Wird nach erfolgreichem Start aufgerufen."""
pass
async def on_pre_stop(self) -> None:
"""Wird vor dem Stoppen aufgerufen."""
pass
async def on_stop(self) -> None:
"""Hauptstop-Logik."""
self.logger.info("Service gestoppt")
async def on_post_stop(self) -> None:
"""Wird nach dem Stoppen aufgerufen."""
pass
async def on_dependency_ready(self, name: str, service: IService) -> None:
"""Wird aufgerufen wenn eine Abhängigkeit bereit ist."""
self.logger.debug(f"Abhängigkeit {name} bereit")
| Attribut | Typ | Beschreibung |
|---|---|---|
NAME |
str |
Eindeutiger Service-Name |
PRIORITY |
ServicePriority |
Start-Reihenfolge |
GROUP |
ServiceGroup |
Kategorisierung |
DEPENDENCIES |
list[str] |
Benötigte Services |
from trixy_core.service import ServicePriority
class ServicePriority(IntEnum):
CRITICAL = 0 # System-kritisch (Logger, Config)
CORE = 10 # Kern-Services (EventManager)
NETWORK = 20 # Netzwerk-Services (Protocol)
MANAGER = 30 # Manager (SatelliteManager)
PLUGIN = 40 # Plugin-Services
OPTIONAL = 50 # Optionale Services
from trixy_core.service import ServiceGroup
class ServiceGroup(Enum):
CRITICAL = "critical" # System-kritisch
NETWORK = "network" # Netzwerk
MANAGER = "manager" # Manager
AUDIO = "audio" # Audio-Verarbeitung
ML = "ml" # Machine Learning
UTILITY = "utility" # Utilities
PLUGIN = "plugin" # Plugins
| Property | Typ | Beschreibung |
|---|---|---|
application |
Application |
Anwendungsreferenz |
state |
ServiceState |
Aktueller Zustand |
name |
str |
Service-Name |
is_running |
bool |
Service läuft |
Verwaltet Service-Lifecycle und Abhängigkeiten.
from trixy_core.service import ServiceContainer
container = ServiceContainer(application)
# Service registrieren
container.register(MyService)
container.register(AnotherService)
# Alle Services starten
await container.start_all()
# Service abrufen
my_service = container.get("my_service")
# Service-Status prüfen
if container.is_running("my_service"):
print("Service läuft")
# Alle Services stoppen
await container.stop_all()
Methoden:
| Methode | Beschreibung |
|---|---|
register(service_class) |
Service registrieren |
unregister(name) |
Service entfernen |
get(name) |
Service abrufen |
start_all() |
Alle Services starten |
stop_all() |
Alle Services stoppen |
is_running(name) |
Prüfen ob Service läuft |
get_by_group(group) |
Services nach Gruppe |
Verzögerte Service-Initialisierung.
from trixy_core.service import LazyService
class ExpensiveService(IService):
async def on_start(self) -> None:
# Wird erst bei erstem Zugriff ausgeführt
self.model = load_large_model()
# Als Lazy registrieren
lazy = LazyService(ExpensiveService)
container.register_lazy("expensive", lazy)
# Service wird erst hier initialisiert
service = await container.get_lazy("expensive")
Koordinierter Shutdown mit Timeouts.
from trixy_core.service import (
GracefulShutdownManager,
ShutdownConfig,
ShutdownPhase,
)
config = ShutdownConfig(
timeout=30.0, # Gesamttimeout
phase_timeout=10.0, # Timeout pro Phase
force_after=60.0, # Force-Kill nach
drain_connections=True, # Verbindungen abbauen
)
manager = GracefulShutdownManager(container, config)
# Shutdown-Handler registrieren
@manager.on_phase(ShutdownPhase.PRE)
async def pre_shutdown():
await notify_clients("Server wird heruntergefahren")
@manager.on_phase(ShutdownPhase.CLEANUP)
async def cleanup():
await flush_caches()
# Shutdown ausführen
report = await manager.shutdown()
print(f"Shutdown abgeschlossen in {report.duration}s")
print(f"Services gestoppt: {report.stopped_count}")
if report.errors:
for error in report.errors:
print(f"Fehler: {error}")
| Phase | Beschreibung |
|---|---|
PRE |
Vor dem Stoppen |
STOP |
Services stoppen |
CLEANUP |
Aufräumen |
POST |
Nach dem Stoppen |
Fault-Tolerance-Pattern für externe Aufrufe.
from trixy_core.service.circuit_breaker import (
CircuitBreaker,
CircuitBreakerConfig,
CircuitState,
circuit_breaker,
)
# Konfiguration
config = CircuitBreakerConfig(
failure_threshold=5, # Fehler bis OPEN
success_threshold=3, # Erfolge bis CLOSED
timeout=30.0, # Zeit in OPEN
half_open_max=3, # Max Versuche in HALF_OPEN
)
# Circuit Breaker erstellen
breaker = CircuitBreaker("api_service", config)
# Verwendung
try:
async with breaker:
result = await external_api_call()
except CircuitOpenError:
# Circuit ist offen - Fallback verwenden
result = get_cached_result()
from trixy_core.service.circuit_breaker import circuit_breaker
@circuit_breaker(
failure_threshold=3,
timeout=60.0,
fallback=lambda: {"status": "degraded"}
)
async def call_payment_api(amount: float) -> dict:
return await payment_client.charge(amount)
CLOSED ──[Fehler >= threshold]──> OPEN
^ │
│ │
│ [timeout]
│ │
│ v
└──[Erfolge >= threshold]── HALF_OPEN
| Zustand | Beschreibung |
|---|---|
CLOSED |
Normal, Requests gehen durch |
OPEN |
Blockiert, Requests werden abgelehnt |
HALF_OPEN |
Testphase, begrenzte Requests |
Verschiedene Lebenszyklen für Services.
from trixy_core.service.scopes import (
SingletonScope,
TransientScope,
RequestScope,
ScopedScope,
ScopeContext,
)
# Singleton: Eine Instanz pro Container
singleton = SingletonScope()
container.register(MyService, scope=singleton)
# Transient: Neue Instanz pro Abruf
transient = TransientScope()
container.register(TemporaryService, scope=transient)
# Request: Eine Instanz pro Request-Scope
request_scope = RequestScope()
async def handle_request(request):
async with ScopeContext("request"):
# Service-Instanz nur für diesen Request
service = container.get("scoped_service")
return await service.process(request)
| Scope | Instanzen |
|---|---|
SingletonScope |
Eine pro Container |
TransientScope |
Neue pro Abruf |
RequestScope |
Eine pro Request |
ScopedScope |
Eine pro Scope |
Service-Metriken für Monitoring.
from trixy_core.service.metrics import (
MetricsCollector,
ServiceMetrics,
Counter, Gauge, Histogram,
PrometheusReporter,
JSONReporter,
)
# Collector erstellen
collector = MetricsCollector()
# Metriken definieren
requests = Counter("http_requests_total", "HTTP Requests")
active_connections = Gauge("active_connections", "Aktive Verbindungen")
response_time = Histogram(
"response_time_seconds",
"Antwortzeit",
buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 5.0]
)
# Metriken aufzeichnen
requests.inc()
requests.inc(labels={"method": "GET", "path": "/api"})
active_connections.set(42)
active_connections.inc()
active_connections.dec()
with response_time.time():
process_request()
# Export
prometheus = PrometheusReporter(collector)
print(prometheus.export())
json_reporter = JSONReporter(collector)
print(json_reporter.export())
Automatische Metriken für Services.
from trixy_core.service.metrics import ServiceMetrics
class MyService(IService):
async def on_start(self) -> None:
self.metrics = ServiceMetrics(self.name)
async def process(self, data):
with self.metrics.operation("process"):
# Automatisches Timing und Fehler-Tracking
return await self._do_process(data)
Visualisierung von Service-Abhängigkeiten.
from trixy_core.service import DependencyGraph, GraphFormat
graph = DependencyGraph(container)
# Als Text
print(graph.render(GraphFormat.TEXT))
# Als Mermaid-Diagramm
print(graph.render(GraphFormat.MERMAID))
# Als Graphviz DOT
dot = graph.render(GraphFormat.GRAPHVIZ)
with open("dependencies.dot", "w") as f:
f.write(dot)
# Als JSON
json_data = graph.render(GraphFormat.JSON)
Beispiel Mermaid-Output:
graph TD
event_manager --> config_manager
satellite_manager --> event_manager
plugin_manager --> event_manager
plugin_manager --> satellite_manager